From 42433fe22d7c94f9219332f7dfbafcd30e5e6e28 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 8 May 2023 07:05:47 +0000 Subject: [PATCH 001/178] build: dockercontainer.json --- .devcontainer/devcontainer.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..76edfa61 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1 @@ +{"image":"mcr.microsoft.com/devcontainers/universal:2"} \ No newline at end of file From f8caa56be715cf32d2e1963faf3624fd946e88f4 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 17 May 2023 16:23:54 +0300 Subject: [PATCH 002/178] rfc: cleanup --- .editorconfig | 2 - .github/workflows/build-publish-v2.yml | 4 +- .github/workflows/prepare-nuget.yml | 2 +- ...one.Channels.RedisConsumerProvider.csproj} | 6 +- .../RedisConsumerChannel.cs | 10 +- .../RedisConsumerProviderExtensions.cs | 6 +- .../Setting/DelayWhenEmptyBehavior.cs | 2 +- .../Setting/RedisConsumerChannelSetting.cs | 2 +- .../Setting/ResiliencePolicies.cs | 2 +- .../Setting/StaleMessagesClaimingTrigger.cs | 2 +- .../RedisHashStorageStrategy.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes ...one.Channels.RedisProducerProvider.csproj} | 4 +- .../RedisProducerChannel.cs | 4 +- .../RedisProviderExtensions.cs | 6 +- .../RedisHashStorageStrategy.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes ...kbone.Channels.RedisProvider.Common.csproj | 15 + .../EventSourceRedisConnectionFacroty.cs | 2 +- .../IEventSourceRedisConnectionFacroty.cs | 2 +- .../Factory/IRedisConnectionFacrotyBase.cs | 2 +- .../Factory/RedisClientFactory.cs | 4 +- .../Factory/RedisConnectionFacrotyBase.cs | 2 +- .../Factory/RedisCredentialsKeys.cs | 4 +- .../RedisChannelConstants.cs | 2 +- .../RedisCommonProviderExtensions.cs | 2 +- .../RedisDiExtensions.cs | 2 +- .../RedisTelemetryrExtensions.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes ...ckbone.Channels.RedisConsumerProviderr.xml | 286 ----- .../icon.png | Bin 3361 -> 0 bytes ...ackbone.Channels.RedisProducerProvider.xml | 136 --- .../icon.png | Bin 3361 -> 0 bytes ...kbone.Channels.RedisProvider.Common.csproj | 15 - ...Backbone.Channels.RedisProvider.Common.xml | 116 --- .../icon.png | Bin 3361 -> 0 bytes ...ne.Channels.S3StoreConsumerProvider.csproj | 14 + .../S3ConsumerStorageStrategy.cs | 6 +- .../S3ConsumerStorageStrategyExtension.cs | 6 +- .../icon.png | Bin 0 -> 58881 bytes ...ne.Channels.S3StoreProducerProvider.csproj | 9 + .../S3ProducerStorageStrategy.cs | 6 +- .../S3ProducerStorageStrategyExtension.cs | 4 +- .../icon.png | Bin 0 -> 58881 bytes .../BlobResponse.cs | 2 +- .../Constants.cs | 2 +- ...one.Channels.S3StoreProvider.Common.csproj | 14 + .../IS3Repository.cs | 2 +- .../IS3RepositoryFactory.cs | 2 +- .../S3EnvironmentConvention.cs | 2 +- .../S3Options.cs | 2 +- .../S3Repository.cs | 4 +- .../S3RepositoryFactory.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes ...ne.Channels.S3StoreConsumerProvider.csproj | 14 - ...bone.Channels.S3StoreConsumerProviderr.xml | 62 -- .../icon.png | Bin 3361 -> 0 bytes ...ne.Channels.S3StoreProducerProvider.csproj | 9 - ...kbone.Channels.S3StoreProducerProvider.xml | 63 -- .../icon.png | Bin 3361 -> 0 bytes ...one.Channels.S3StoreProvider.Common.csproj | 14 - ...ckbone.Channels.S3StoreProvider.Common.xml | 265 ----- .../icon.png | Bin 3361 -> 0 bytes .../Ack/Ack.cs | 4 +- .../Ack/AckBehavior.cs | 2 +- .../Ack/AckOnce.cs | 2 +- .../Ack/IAck.cs | 2 +- .../Attributes.cs | 5 + .../Building/IConsumerFilterBuilder.cs | 2 +- .../Builder/Building/IConsumerHooksBuilder.cs | 4 +- .../Building/IConsumerOptionsBuilder.cs | 2 +- .../Builder/Building/IConsumerReadyBuilder.cs | 4 +- .../IConsumerStorageStrategyWithFilter.cs | 2 +- .../Building/IConsumerStoreStrategyBuilder.cs | 2 +- .../Building/IConsumerSubscribeBuilder.cs | 2 +- .../ConsumerAsyncEnumerableJsonOptions.cs | 2 +- .../ConsumerAsyncEnumerableOptions.cs | 2 +- .../Building/Receiver/IConsumerIterator.cs | 4 +- .../Receiver/IConsumerIteratorCommands.cs | 2 +- .../Building/Receiver/IConsumerReceiver.cs | 4 +- .../Receiver/IConsumerReceiverCommands.cs | 2 +- .../Route/IConsumerEnvironmentBuilder.cs | 2 +- .../Route/IConsumerEnvironmentOfBuilder.cs | 2 +- .../Route/IConsumerPartitionBuilder.cs | 2 +- .../Building/Route/IConsumerShardBuilder.cs | 2 +- .../Building/Route/IConsumerShardOfBuilder.cs | 2 +- .../Builder/IConsumer.cs | 2 +- .../Builder/IConsumerBuilder.cs | 4 +- .../Builder/IConsumerLifetime.cs | 4 +- .../Builder/IConsumerPlan.cs | 4 +- .../Builder/IConsumerPlanBase.cs | 2 +- .../Builder/IConsumerPlanBuilder.cs | 4 +- .../ClaimingTrigger.cs | 2 +- .../ConsumerMetadata.cs | 2 +- .../ConsumerOptions.cs | 4 +- .../Enums/MultiConsumerBehavior.cs | 2 +- .../Enums/PartialConsumerBehavior.cs | 2 +- ...Source.Backbone.Consumers.Contracts.csproj | 16 + .../EventSourceConsumer.deprecated.cs | 2 +- .../IConsumerBridge.cs | 2 +- .../IConsumerEntityMapper.cs | 2 +- .../ISubscriptionBridge.cs | 2 +- .../Interceptors/ConsumerInterceptorBridge.cs | 2 +- .../Interceptors/IConsumerAsyncInterceptor.cs | 4 +- .../Interceptors/IConsumerInterceptor.cs | 4 +- .../Provider/IConsumerChannelProvider.cs | 2 +- .../Provider/IConsumerStorageStrategy.cs | 2 +- .../ConsumerSegmentationStrategyBridge.cs | 2 +- .../IConsumerAsyncSegmenationStrategy.cs | 2 +- .../IConsumerSegmenationStrategy.cs | 2 +- .../TelemetryrExtensions.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes .../ConsumerBase.EventSourceSubscriber.cs | 8 +- .../Builder/ConsumerBase.cs | 2 +- .../Builder/ConsumerBuilder.Iterator.cs | 4 +- .../Builder/ConsumerBuilder.Receiver.cs | 4 +- .../Builder/ConsumerBuilder.cs | 6 +- .../Builder/ConsumerPlan.cs | 6 +- .../Builder/FilteredStorageStrategy.cs | 4 +- .../ConsumerDefaultSegmentationStrategy.cs | 4 +- .../EventSource.Backbone.Consumers.csproj} | 6 +- .../EventSource.Backbone.Consumers/icon.png | Bin 0 -> 58881 bytes .../Attributes.cs | 5 - ...Source.Backbone.Consumers.Contracts.csproj | 16 - ...entSource.Backbone.Consumers.Contracts.xml | 982 ----------------- .../icon.png | Bin 3361 -> 0 bytes .../Weknow.EventSource.Backbone.xml | 622 ----------- .../icon.png | Bin 3361 -> 0 bytes Directory.Build.props | 13 +- .../Attributes/EventSourceGenType.cs | 2 +- .../Attributes/EventSourceVersion.cs | 4 +- .../GenerateEventSourceAttribute.cs | 4 +- .../GenerateEventSourceBaseAttribute.cs | 2 +- .../GenerateEventSourceBridgeAttribute.cs | 2 +- .../Bucket.cs | 14 +- .../BucketJsonConverter.cs | 4 +- .../Entities/Announcement/Announcement.cs | 2 +- .../Entities/Announcement/AnnouncementData.cs | 2 +- .../Announcement/EventSourceJsonContext.cs | 2 +- .../Entities/Announcement/MessageOrigin.cs | 2 +- .../Entities/Announcement/Metadata.cs | 2 +- .../Entities/EventKey/EventKey.cs | 2 +- .../Entities/EventKey/EventKeys.cs | 2 +- .../Enums/EventBucketCategories.cs | 2 +- .../Env.cs | 2 +- .../EventSource.Backbone.Contracts.csproj | 6 +- .../EventSourceConstants.cs | 4 +- .../EventSourceFallbakLogger.cs | 2 +- .../EventSourceOptions.cs | 6 +- .../Interfaces/IDataSerializer.cs | 2 +- .../Interfaces/IInterceptorName.cs | 2 +- .../Interfaces/IPlanRoute.cs | 2 +- .../Interfaces/IWithCancellation.cs | 2 +- .../JsonDataSerializer.cs | 6 +- .../TelemetryrExtensions.cs | 2 +- EventSource.Backbone.Contracts/icon.png | Bin 0 -> 58881 bytes ...e.Backbone.sln => EventSource.Backbone.sln | 34 +- .../EventSource.Backbone.csproj | 2 +- .../EventSource.Backbone.xml | 2 +- EventSource.Backbone/icon.png | Bin 0 -> 58881 bytes NuGet.config | 2 + .../Building/IProducerBuilderEnvironment.cs | 2 +- .../Building/IProducerEnvironmentBuilder.cs | 2 +- .../Builder/Building/IProducerHooksBuilder.cs | 2 +- .../Building/IProducerLoggerBuilder.cs | 2 +- .../Building/IProducerOptionsBuilder.cs | 2 +- .../Building/IProducerPartitionBuilder.cs | 2 +- .../Builder/Building/IProducerRawBuilder.cs | 2 +- .../Builder/Building/IProducerShardBuilder.cs | 2 +- .../Building/IProducerSpecializeBuilder.cs | 2 +- .../IProducerStorageStrategyWithFilter.cs | 2 +- .../Building/IProducerStoreStrategyBuilder.cs | 6 +- .../Builder/Building/IRawProducer.cs | 2 +- .../Override/IProducerOverrideBuildBuilder.cs | 2 +- .../Override/IProducerOverrideBuilder.cs | 4 +- .../IProducerOverrideEnvironmentBuilder.cs | 2 +- .../IProducerOverridePartitionBuilder.cs | 2 +- .../Override/IProducerOverrideShardBuilder.cs | 2 +- .../Builder/Building/RawProducerOptions.cs | 2 +- .../Builder/Building/RouteAssignmentType.cs | 2 +- .../Builder/IProducerBuilder.cs | 4 +- ...ource.Backbone.Producers.Contracts.csproj} | 2 +- .../Interceptors/IProducerAsyncInterceptor.cs | 4 +- .../Interceptors/IProducerInterceptor.cs | 4 +- .../Interceptors/ProducerInterceptorBridge.cs | 2 +- .../Plan/IProducerPlan.cs | 4 +- .../Plan/IProducerPlanBase.cs | 2 +- .../Plan/IProducerPlanBuilder.cs | 6 +- .../Plan/ProducerPlan.cs | 8 +- .../ProducerExtensions.cs | 6 +- .../ProducerPipeline.cs | 2 +- .../Provider/IProducerChannelProvider.cs | 2 +- .../Provider/IProducerStorageStrategy.cs | 2 +- .../IProducerAsyncSegmentationStrategy.cs | 2 +- .../IProducerSegmentationStrategy.cs | 2 +- .../ProducerSegmentationStrategyBridge.cs | 2 +- .../TelemetryrExtensions.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes .../Builder/FilteredStorageStrategy.cs | 2 +- .../Builder/ProducerBuilder.cs | 6 +- .../EventSource.Backbone.Producers.csproj | 12 + .../ProducerDefaultSegmentationStrategy.cs | 2 +- .../EventSource.Backbone.Producers/icon.png | Bin 0 -> 58881 bytes ...entSource.Backbone.Producers.Contracts.xml | 985 ------------------ .../icon.png | Bin 3361 -> 0 bytes ...know.EventSource.Backbone.Producers.csproj | 12 - .../Weknow.EventSource.Backbone.Producers.xml | 286 ----- .../icon.png | Bin 3361 -> 0 bytes .../.editorconfig | 0 .../Class1.cs | 4 +- .../Contracts/IEventFlow.cs | 2 +- .../Contracts/IEventFlowStage1.cs | 2 +- .../Contracts/IEventFlowStage2.cs | 2 +- .../Contracts/ISequenceOperations.cs | 2 +- .../Contracts}/Inheritance/IFlowA.cs | 2 +- .../Contracts/Inheritance/IFlowAB.cs | 2 +- .../Contracts/Inheritance/IFlowB.cs | 2 +- .../DeleteKeysTests.cs | 4 +- .../EndToEndExplicitTests.cs | 6 +- .../EndToEndStressTests.cs | 8 +- .../EndToEndTests.cs | 12 +- .../Entities/Person.cs | 5 + .../Entities/User.cs | 2 +- ...entSource.Backbone.IntegrationTests.csproj | 64 ++ .../InheritanceS3StoreStrategyTests.cs | 2 +- .../InheritanceTests.cs | 53 +- .../MigrationReceiverTest.cs | 4 +- .../MigrationTest.cs | 10 +- .../S3StoreStrategyExplicitTests.cs | 2 +- .../S3StoreStrategyStressTests .cs | 2 +- .../S3StoreStrategyTests.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes .../payload.json | 0 .../person.json | 0 .../.editorconfig | 0 .../EventSourceApiConsumerDesignTests.cs | 4 +- .../EventSourceApiProducerDesignTests.cs | 6 +- .../ApiDesign/TestApiExtensions.cs | 4 +- .../Channels/ConsumerTestChannel.cs | 2 +- .../Channels/ProducerTestChannel.cs | 4 +- .../ConsumerBuilderTests.cs | 4 +- .../Contracts/ISequenceOperations.cs | 2 +- .../Contracts/ISimpleEvent.cs | 2 +- .../Contracts/ISimpleEventTag.cs | 2 +- .../EndToEndTests.cs | 6 +- .../Entities/User.cs | 2 +- .../EventIdTests.cs | 2 +- .../EventSource.Backbone.UnitTests.csproj} | 16 +- .../SequenceOfProducerFactory.cs | 2 +- .../SequenceOfProducerFactoryExtensions.cs | 4 +- .../SequenceOperationsConsumer.cs | 2 +- .../SequenceOperationsProducerFactory.cs | 2 +- ...enceOperationsProducerFactoryExtensions.cs | 4 +- .../InheritGenerationTest.cs | 4 +- .../ProducerBuilderTests.cs | 6 +- .../S3OptionsTests.cs | 2 +- .../SerializationTests.cs | 5 +- .../SourceMigrationTests.cs | 6 +- .../StoreStrategyTests.cs | 6 +- .../Subscriptions/SimpleEventSubscription.cs | 6 +- .../SimpleEventSubscriptionBase.cs | 4 +- .../SimpleEventSubscriptionBridge.cs | 6 +- ...SimpleEventSubscriptionBridgeExtensions.cs | 6 +- .../SimpleEventSubscriptionFromGen.cs | 6 +- Tests/EventSource.Backbone.UnitTests/icon.png | Bin 0 -> 58881 bytes .../.editorconfig | 0 .../AspCoreExtensions.cs | 3 +- .../Contracts/IEventFlow.cs | 6 +- .../Contracts/IEventsMigration.cs | 2 +- .../Contracts/IVersionedEvents.cs | 2 +- .../Controllers/EventSourceApiController.cs | 2 +- .../Controllers/MigrationController.cs | 2 +- .../Controllers/TestController.cs | 2 +- .../Dockerfile | 0 .../Dockerfile.develop | 0 .../Entities/Address.cs | 2 +- .../Entities/Person.cs | 2 +- .../EventSource.Backbone.WebEventTest.csproj} | 14 +- .../Jobs/HostedServiceBase.cs | 0 .../Jobs/MicroDemoJob.cs | 4 +- .../Jobs/MigrationJob.cs | 4 +- .../Program.cs | 6 +- .../Properties/launchSettings.json | 0 .../RegisterEventSourceExtensions.cs | 4 +- .../TestSampler.cs | 0 .../appsettings.Development.json | 0 .../appsettings.json | 0 .../azds.yaml | 0 .../.helmignore | 0 .../Chart.yaml | 0 .../templates/NOTES.txt | 0 .../templates/_helpers.tpl | 0 .../templates/deployment.yaml | 0 .../templates/ingress.yaml | 0 .../templates/secrets.yaml | 0 .../templates/service.yaml | 0 .../values.yaml | 0 .../icon.png | Bin 0 -> 58881 bytes .../Entities/Person.cs | 5 - ...entSource.Backbone.IntegrationTests.csproj | 64 -- .../icon.png | Bin 3361 -> 0 bytes .../icon.png | Bin 3361 -> 0 bytes .../icon.png | Bin 3361 -> 0 bytes .../Weknow.EventSource.Backbone.Contracts.xml | 608 ----------- .../icon.png | Bin 3361 -> 0 bytes .../icon.png | Bin 3361 -> 0 bytes Weknow.EventSource.Backbone/icon.png | Bin 3361 -> 0 bytes ...ntSource.Backbone.SrcGen.Playground.csproj | 24 + .../ISample.cs | 2 +- .../Inheritance/IFlowA.cs | 2 +- .../Inheritance/IFlowAB.cs | 2 +- .../Inheritance/IFlowB.cs | 2 +- .../Program.cs | 2 +- .../icon.png | Bin 0 -> 58881 bytes .../EventSource.Backbone.SrcGen.csproj} | 2 +- .../Concrete/BridgeIncrementalGenerator.cs | 10 +- .../Concrete/ContractIncrementalGenerator.cs | 6 +- .../EntitiesAndHelpers/EntityGenerator.cs | 6 +- .../EntitiesAndHelpers/GenInstruction.cs | 2 +- .../EntitiesAndHelpers/KindFilter.cs | 2 +- .../Generators/GeneratorIncrementalBase.cs | 12 +- .../Helper.cs | 2 +- .../Properties/launchSettings.json | 0 .../RoslynHelper.cs | 0 .../SyntaxReceiverResult.cs | 2 +- src-gen/EventSource.Backbone.SrcGen/icon.png | Bin 0 -> 58881 bytes ...ntSource.Backbone.SrcGen.Playground.csproj | 24 - .../icon.png | Bin 3361 -> 0 bytes .../icon.png | Bin 3361 -> 0 bytes 329 files changed, 632 insertions(+), 5023 deletions(-) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider.csproj => EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj} (50%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/RedisConsumerChannel.cs (99%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/RedisConsumerProviderExtensions.cs (97%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/Setting/DelayWhenEmptyBehavior.cs (94%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/Setting/RedisConsumerChannelSetting.cs (95%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/Setting/ResiliencePolicies.cs (98%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/Setting/StaleMessagesClaimingTrigger.cs (94%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisConsumerProvider => EventSource.Backbone.Channels.RedisConsumerProvider}/StorageStrategies/RedisHashStorageStrategy.cs (98%) create mode 100644 Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/icon.png rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProducerProvider/Weknow.EventSource.Backbone.Channels.RedisProducerProvider.csproj => EventSource.Backbone.Channels.RedisProducerProvider/EventSource.Backbone.Channels.RedisProducerProvider.csproj} (62%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProducerProvider => EventSource.Backbone.Channels.RedisProducerProvider}/RedisProducerChannel.cs (97%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProducerProvider => EventSource.Backbone.Channels.RedisProducerProvider}/RedisProviderExtensions.cs (96%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProducerProvider => EventSource.Backbone.Channels.RedisProducerProvider}/StorageStrategies/RedisHashStorageStrategy.cs (98%) create mode 100644 Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/icon.png create mode 100644 Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/EventSource.Backbone.Channels.RedisProvider.Common.csproj rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/Factory/EventSourceRedisConnectionFacroty.cs (98%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/Factory/IEventSourceRedisConnectionFacroty.cs (80%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/Factory/IRedisConnectionFacrotyBase.cs (91%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/Factory/RedisClientFactory.cs (98%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/Factory/RedisConnectionFacrotyBase.cs (99%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/Factory/RedisCredentialsKeys.cs (79%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/RedisChannelConstants.cs (92%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/RedisCommonProviderExtensions.cs (99%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/RedisDiExtensions.cs (95%) rename Channels/REDIS/{Weknow.EventSource.Backbone.Channels.RedisProvider.Common => EventSource.Backbone.Channels.RedisProvider.Common}/RedisTelemetryrExtensions.cs (96%) create mode 100644 Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/icon.png delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Weknow.EventSource.Backbone.Channels.RedisConsumerProviderr.xml delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/icon.png delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/Weknow.EventSource.Backbone.Channels.RedisProducerProvider.xml delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/icon.png delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Weknow.EventSource.Backbone.Channels.RedisProvider.Common.csproj delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Weknow.EventSource.Backbone.Channels.RedisProvider.Common.xml delete mode 100644 Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/icon.png create mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider => EventSource.Backbone.Channels.S3StoreConsumerProvider}/S3ConsumerStorageStrategy.cs (96%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider => EventSource.Backbone.Channels.S3StoreConsumerProvider}/S3ConsumerStorageStrategyExtension.cs (90%) create mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png create mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/EventSource.Backbone.Channels.S3StoreProducerProvider.csproj rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider => EventSource.Backbone.Channels.S3StoreProducerProvider}/S3ProducerStorageStrategy.cs (96%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider => EventSource.Backbone.Channels.S3StoreProducerProvider}/S3ProducerStorageStrategyExtension.cs (95%) create mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/BlobResponse.cs (99%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/Constants.cs (75%) create mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/IS3Repository.cs (98%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/IS3RepositoryFactory.cs (83%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/S3EnvironmentConvention.cs (91%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/S3Options.cs (96%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/S3Repository.cs (99%) rename Channels/S3/{Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common => EventSource.Backbone.Channels.S3StoreProvider.Common}/S3RepositoryFactory.cs (98%) create mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProviderr.xml delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider.csproj delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider.xml delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common.csproj delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common.xml delete mode 100644 Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Ack/Ack.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Ack/AckBehavior.cs (91%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Ack/AckOnce.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Ack/IAck.cs (94%) create mode 100644 Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerFilterBuilder.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerHooksBuilder.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerOptionsBuilder.cs (89%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerReadyBuilder.cs (89%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerStorageStrategyWithFilter.cs (93%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerStoreStrategyBuilder.cs (95%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/IConsumerSubscribeBuilder.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs (88%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs (95%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Receiver/IConsumerIterator.cs (91%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Receiver/IConsumerIteratorCommands.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Receiver/IConsumerReceiver.cs (77%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Receiver/IConsumerReceiverCommands.cs (96%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Route/IConsumerEnvironmentBuilder.cs (85%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs (91%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Route/IConsumerPartitionBuilder.cs (94%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Route/IConsumerShardBuilder.cs (81%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/Building/Route/IConsumerShardOfBuilder.cs (93%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/IConsumer.cs (84%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/IConsumerBuilder.cs (85%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/IConsumerLifetime.cs (73%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/IConsumerPlan.cs (94%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/IConsumerPlanBase.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Builder/IConsumerPlanBuilder.cs (90%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/ClaimingTrigger.cs (96%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/ConsumerMetadata.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/ConsumerOptions.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Enums/MultiConsumerBehavior.cs (95%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Enums/PartialConsumerBehavior.cs (95%) create mode 100644 Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/EventSourceConsumer.deprecated.cs (96%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/IConsumerBridge.cs (93%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/IConsumerEntityMapper.cs (94%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/ISubscriptionBridge.cs (93%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Interceptors/ConsumerInterceptorBridge.cs (96%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Interceptors/IConsumerAsyncInterceptor.cs (87%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Interceptors/IConsumerInterceptor.cs (86%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Provider/IConsumerChannelProvider.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Provider/IConsumerStorageStrategy.cs (96%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Segmentation/ConsumerSegmentationStrategyBridge.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Segmentation/IConsumerAsyncSegmenationStrategy.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/Segmentation/IConsumerSegmenationStrategy.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers.Contracts => EventSource.Backbone.Consumers.Contracts}/TelemetryrExtensions.cs (97%) create mode 100644 Consumers/EventSource.Backbone.Consumers.Contracts/icon.png rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/ConsumerBase.EventSourceSubscriber.cs (98%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/ConsumerBase.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/ConsumerBuilder.Iterator.cs (99%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/ConsumerBuilder.Receiver.cs (97%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/ConsumerBuilder.cs (99%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/ConsumerPlan.cs (99%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/Builder/FilteredStorageStrategy.cs (96%) rename Consumers/{Weknow.EventSource.Backbone.Consumers => EventSource.Backbone.Consumers}/ConsumerDefaultSegmentationStrategy.cs (94%) rename Consumers/{Weknow.EventSource.Backbone.Consumers/Weknow.EventSource.Backbone.Consumers.csproj => EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj} (61%) create mode 100644 Consumers/EventSource.Backbone.Consumers/icon.png delete mode 100644 Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Attributes.cs delete mode 100644 Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Weknow.EventSource.Backbone.Consumers.Contracts.csproj delete mode 100644 Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Weknow.EventSource.Backbone.Consumers.Contracts.xml delete mode 100644 Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/icon.png delete mode 100644 Consumers/Weknow.EventSource.Backbone.Consumers/Weknow.EventSource.Backbone.xml delete mode 100644 Consumers/Weknow.EventSource.Backbone.Consumers/icon.png rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Attributes/EventSourceGenType.cs (79%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Attributes/EventSourceVersion.cs (91%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Attributes/GenerateEventSourceAttribute.cs (81%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Attributes/GenerateEventSourceBaseAttribute.cs (96%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Attributes/GenerateEventSourceBridgeAttribute.cs (91%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Bucket.cs (94%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/BucketJsonConverter.cs (95%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/Announcement/Announcement.cs (97%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/Announcement/AnnouncementData.cs (98%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/Announcement/EventSourceJsonContext.cs (87%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/Announcement/MessageOrigin.cs (90%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/Announcement/Metadata.cs (99%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/EventKey/EventKey.cs (98%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Entities/EventKey/EventKeys.cs (99%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Enums/EventBucketCategories.cs (85%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Env.cs (98%) rename Weknow.EventSource.Backbone.Contracts/Weknow.EventSource.Backbone.Contracts.csproj => EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj (70%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/EventSourceConstants.cs (88%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/EventSourceFallbakLogger.cs (98%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/EventSourceOptions.cs (88%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Interfaces/IDataSerializer.cs (94%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Interfaces/IInterceptorName.cs (88%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Interfaces/IPlanRoute.cs (97%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/Interfaces/IWithCancellation.cs (90%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/JsonDataSerializer.cs (88%) rename {Weknow.EventSource.Backbone.Contracts => EventSource.Backbone.Contracts}/TelemetryrExtensions.cs (97%) create mode 100644 EventSource.Backbone.Contracts/icon.png rename Weknow.EventSource.Backbone.sln => EventSource.Backbone.sln (70%) rename Weknow.EventSource.Backbone/Weknow.EventSource.Backbone.csproj => EventSource.Backbone/EventSource.Backbone.csproj (76%) rename Weknow.EventSource.Backbone/Weknow.EventSource.Backbone.xml => EventSource.Backbone/EventSource.Backbone.xml (65%) create mode 100644 EventSource.Backbone/icon.png rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerBuilderEnvironment.cs (89%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerEnvironmentBuilder.cs (83%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerHooksBuilder.cs (99%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerLoggerBuilder.cs (92%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerOptionsBuilder.cs (88%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerPartitionBuilder.cs (95%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerRawBuilder.cs (89%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerShardBuilder.cs (93%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerSpecializeBuilder.cs (97%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerStorageStrategyWithFilter.cs (94%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IProducerStoreStrategyBuilder.cs (86%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/IRawProducer.cs (90%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/Override/IProducerOverrideBuildBuilder.cs (95%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/Override/IProducerOverrideBuilder.cs (90%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs (93%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/Override/IProducerOverridePartitionBuilder.cs (93%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/Override/IProducerOverrideShardBuilder.cs (93%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/RawProducerOptions.cs (88%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/Building/RouteAssignmentType.cs (81%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Builder/IProducerBuilder.cs (92%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts/Weknow.EventSource.Backbone.Producers.Contracts.csproj => EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj} (71%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Interceptors/IProducerAsyncInterceptor.cs (88%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Interceptors/IProducerInterceptor.cs (85%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Interceptors/ProducerInterceptorBridge.cs (96%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Plan/IProducerPlan.cs (89%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Plan/IProducerPlanBase.cs (97%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Plan/IProducerPlanBuilder.cs (88%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Plan/ProducerPlan.cs (98%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/ProducerExtensions.cs (94%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/ProducerPipeline.cs (99%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Provider/IProducerChannelProvider.cs (94%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Provider/IProducerStorageStrategy.cs (97%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Segmentation/IProducerAsyncSegmentationStrategy.cs (98%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Segmentation/IProducerSegmentationStrategy.cs (97%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/Segmentation/ProducerSegmentationStrategyBridge.cs (98%) rename Producers/{Weknow.EventSource.Backbone.Producers.Contracts => EventSource.Backbone.Producers.Contracts}/TelemetryrExtensions.cs (97%) create mode 100644 Producers/EventSource.Backbone.Producers.Contracts/icon.png rename Producers/{Weknow.EventSource.Backbone.Producers => EventSource.Backbone.Producers}/Builder/FilteredStorageStrategy.cs (98%) rename Producers/{Weknow.EventSource.Backbone.Producers => EventSource.Backbone.Producers}/Builder/ProducerBuilder.cs (99%) create mode 100644 Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj rename Producers/{Weknow.EventSource.Backbone.Producers => EventSource.Backbone.Producers}/ProducerDefaultSegmentationStrategy.cs (93%) create mode 100644 Producers/EventSource.Backbone.Producers/icon.png delete mode 100644 Producers/Weknow.EventSource.Backbone.Producers.Contracts/Weknow.EventSource.Backbone.Producers.Contracts.xml delete mode 100644 Producers/Weknow.EventSource.Backbone.Producers.Contracts/icon.png delete mode 100644 Producers/Weknow.EventSource.Backbone.Producers/Weknow.EventSource.Backbone.Producers.csproj delete mode 100644 Producers/Weknow.EventSource.Backbone.Producers/Weknow.EventSource.Backbone.Producers.xml delete mode 100644 Producers/Weknow.EventSource.Backbone.Producers/icon.png rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/.editorconfig (100%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Class1.cs (84%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Contracts/IEventFlow.cs (92%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Contracts/IEventFlowStage1.cs (86%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Contracts/IEventFlowStage2.cs (89%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Contracts/ISequenceOperations.cs (94%) rename {src-gen/Weknow.EventSource.Backbone.SrcGen.Playground => Tests/EventSource.Backbone.IntegrationTests/Contracts}/Inheritance/IFlowA.cs (72%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Contracts/Inheritance/IFlowAB.cs (75%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Contracts/Inheritance/IFlowB.cs (74%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/DeleteKeysTests.cs (94%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/EndToEndExplicitTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/EndToEndStressTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/EndToEndTests.cs (99%) create mode 100644 Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/Entities/User.cs (93%) create mode 100644 Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/InheritanceS3StoreStrategyTests.cs (92%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/InheritanceTests.cs (89%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/MigrationReceiverTest.cs (98%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/MigrationTest.cs (98%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/S3StoreStrategyExplicitTests.cs (88%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/S3StoreStrategyStressTests .cs (92%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/S3StoreStrategyTests.cs (92%) create mode 100644 Tests/EventSource.Backbone.IntegrationTests/icon.png rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/payload.json (100%) rename Tests/{Weknow.EventSource.Backbone.IntegrationTests => EventSource.Backbone.IntegrationTests}/person.json (100%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/.editorconfig (100%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/ApiDesign/EventSourceApiConsumerDesignTests.cs (95%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/ApiDesign/EventSourceApiProducerDesignTests.cs (97%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/ApiDesign/TestApiExtensions.cs (96%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Channels/ConsumerTestChannel.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Channels/ProducerTestChannel.cs (92%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/ConsumerBuilderTests.cs (95%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Contracts/ISequenceOperations.cs (93%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Contracts/ISimpleEvent.cs (94%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Contracts/ISimpleEventTag.cs (88%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/EndToEndTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Entities/User.cs (95%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/EventIdTests.cs (97%) rename Tests/{Weknow.EventSource.Backbone.UnitTests/Weknow.EventSource.Backbone.UnitTests.csproj => EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj} (50%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs (97%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs (84%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Factory/SequenceOperations/SequenceOperationsConsumer.cs (95%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs (77%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/InheritGenerationTest.cs (87%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/ProducerBuilderTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/S3OptionsTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/SerializationTests.cs (95%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/SourceMigrationTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/StoreStrategyTests.cs (98%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Subscriptions/SimpleEventSubscription.cs (77%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionBase.cs (95%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionBridge.cs (90%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs (72%) rename Tests/{Weknow.EventSource.Backbone.UnitTests => EventSource.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionFromGen.cs (81%) create mode 100644 Tests/EventSource.Backbone.UnitTests/icon.png rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/.editorconfig (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/AspCoreExtensions.cs (98%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Contracts/IEventFlow.cs (87%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Contracts/IEventsMigration.cs (90%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Contracts/IVersionedEvents.cs (95%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Controllers/EventSourceApiController.cs (98%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Controllers/MigrationController.cs (93%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Controllers/TestController.cs (97%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Dockerfile (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Dockerfile.develop (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Entities/Address.cs (58%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Entities/Person.cs (58%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest/Weknow.EventSource.Backbone.WebEventTest.csproj => EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj} (55%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Jobs/HostedServiceBase.cs (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Jobs/MicroDemoJob.cs (97%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Jobs/MigrationJob.cs (96%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Program.cs (96%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/Properties/launchSettings.json (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/RegisterEventSourceExtensions.cs (97%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/TestSampler.cs (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/appsettings.Development.json (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/appsettings.json (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/azds.yaml (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/.helmignore (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml (100%) rename Tests/{Weknow.EventSource.Backbone.WebEventTest => EventSource.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/values.yaml (100%) create mode 100644 Tests/EventSource.Backbone.WebEventTest/icon.png delete mode 100644 Tests/Weknow.EventSource.Backbone.IntegrationTests/Entities/Person.cs delete mode 100644 Tests/Weknow.EventSource.Backbone.IntegrationTests/Weknow.EventSource.Backbone.IntegrationTests.csproj delete mode 100644 Tests/Weknow.EventSource.Backbone.IntegrationTests/icon.png delete mode 100644 Tests/Weknow.EventSource.Backbone.UnitTests/icon.png delete mode 100644 Tests/Weknow.EventSource.Backbone.WebEventTest/icon.png delete mode 100644 Weknow.EventSource.Backbone.Contracts/Weknow.EventSource.Backbone.Contracts.xml delete mode 100644 Weknow.EventSource.Backbone.Contracts/icon.png delete mode 100644 Weknow.EventSource.Backbone.Producers.Contracts/icon.png delete mode 100644 Weknow.EventSource.Backbone/icon.png create mode 100644 src-gen/EventSource.Backbone.SrcGen.Playground/EventSource.Backbone.SrcGen.Playground.csproj rename src-gen/{Weknow.EventSource.Backbone.SrcGen.Playground => EventSource.Backbone.SrcGen.Playground}/ISample.cs (88%) rename {Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts => src-gen/EventSource.Backbone.SrcGen.Playground}/Inheritance/IFlowA.cs (72%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen.Playground => EventSource.Backbone.SrcGen.Playground}/Inheritance/IFlowAB.cs (70%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen.Playground => EventSource.Backbone.SrcGen.Playground}/Inheritance/IFlowB.cs (74%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen.Playground => EventSource.Backbone.SrcGen.Playground}/Program.cs (72%) create mode 100644 src-gen/EventSource.Backbone.SrcGen.Playground/icon.png rename src-gen/{Weknow.EventSource.Backbone.SrcGen/Weknow.EventSource.Backbone.SrcGen.csproj => EventSource.Backbone.SrcGen/EventSource.Backbone.SrcGen.csproj} (94%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Generators/Concrete/BridgeIncrementalGenerator.cs (98%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Generators/Concrete/ContractIncrementalGenerator.cs (95%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Generators/EntitiesAndHelpers/EntityGenerator.cs (98%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Generators/EntitiesAndHelpers/GenInstruction.cs (91%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Generators/EntitiesAndHelpers/KindFilter.cs (57%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Generators/GeneratorIncrementalBase.cs (96%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Helper.cs (98%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/Properties/launchSettings.json (100%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/RoslynHelper.cs (100%) rename src-gen/{Weknow.EventSource.Backbone.SrcGen => EventSource.Backbone.SrcGen}/SyntaxReceiverResult.cs (99%) create mode 100644 src-gen/EventSource.Backbone.SrcGen/icon.png delete mode 100644 src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Weknow.EventSource.Backbone.SrcGen.Playground.csproj delete mode 100644 src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/icon.png delete mode 100644 src-gen/Weknow.EventSource.Backbone.SrcGen/icon.png diff --git a/.editorconfig b/.editorconfig index 88723dd9..81030afd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,2 @@ [*.cs] -# IDE0008: Use explicit type -csharp_style_var_elsewhere = true diff --git a/.github/workflows/build-publish-v2.yml b/.github/workflows/build-publish-v2.yml index 6cd8ee3c..07c27009 100644 --- a/.github/workflows/build-publish-v2.yml +++ b/.github/workflows/build-publish-v2.yml @@ -35,7 +35,7 @@ jobs: uses: EndBug/add-and-commit@v7 with: author_name: CI/CD - author_email: bnaya@weknow.network.com + author_email: ci.bnaya@gmail.com message: 'Increment Version' add: 'Directory.Build.props' @@ -50,7 +50,7 @@ jobs: - name: Build run: dotnet build --configuration ${{ env.BUILD_CONFIG }} --no-restore - name: Test - run: dotnet test Tests/Weknow.EventSource.Backbone.UnitTests --configuration ${{ env.BUILD_CONFIG }} --no-restore --no-build --verbosity normal + run: dotnet test Tests/EventSource.Backbone.UnitTests --configuration ${{ env.BUILD_CONFIG }} --no-restore --no-build --verbosity normal - name: Push generated package to GitHub registry run: dotnet nuget push ./**/*.nupkg -k ${{ secrets.NUGET_PUBLISH }} -s https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.github/workflows/prepare-nuget.yml b/.github/workflows/prepare-nuget.yml index d09e0633..ea49353f 100644 --- a/.github/workflows/prepare-nuget.yml +++ b/.github/workflows/prepare-nuget.yml @@ -20,6 +20,6 @@ on: jobs: version_increment: - uses: weknow-network/weknow-workflows/.github/workflows/dotnet-increment-version.yml@main + uses: bnayae/open-workflows/.github/workflows/dotnet-increment-version.yml@main \ No newline at end of file diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj similarity index 50% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider.csproj rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj index 32920e42..a47e03fa 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj @@ -1,10 +1,10 @@  - + - - + + diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs similarity index 99% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 6d6ff9f5..2dfd3568 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -7,16 +7,16 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Channels.RedisProvider.Common; -using Weknow.EventSource.Backbone.Private; +using EventSource.Backbone.Building; +using EventSource.Backbone.Channels.RedisProvider.Common; +using EventSource.Backbone.Private; using static System.Math; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // TODO: [bnaya 2021-07] MOVE TELEMETRY TO THE BASE CLASSES OF PRODUCER / CONSUME -namespace Weknow.EventSource.Backbone.Channels.RedisProvider +namespace EventSource.Backbone.Channels.RedisProvider { /// /// The redis consumer channel. diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs similarity index 97% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs index ca951683..2d41840d 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs @@ -4,10 +4,10 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Channels.RedisProvider; +using EventSource.Backbone.Building; +using EventSource.Backbone.Channels.RedisProvider; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class RedisConsumerProviderExtensions { diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs similarity index 94% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs index d27513f4..2b0b4001 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs @@ -1,7 +1,7 @@ using static System.Math; -namespace Weknow.EventSource.Backbone.Channels.RedisProvider +namespace EventSource.Backbone.Channels.RedisProvider { /// /// Behavior of delay when empty diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs similarity index 95% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs index 47f987b5..805a723d 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs @@ -1,7 +1,7 @@ using Polly; -namespace Weknow.EventSource.Backbone.Channels.RedisProvider +namespace EventSource.Backbone.Channels.RedisProvider { /// /// Represent specific setting of the consumer channel diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs similarity index 98% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs index aab1487f..51793ec9 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs @@ -1,7 +1,7 @@ using Polly; using Polly.CircuitBreaker; -namespace Weknow.EventSource.Backbone.Channels.RedisProvider +namespace EventSource.Backbone.Channels.RedisProvider { /// /// Define when to claim stale (long waiting) messages from other consumers diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs similarity index 94% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs index ee660a75..b2c6d125 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs @@ -1,6 +1,6 @@ // TODO: [bnaya 2021-02] use Record -namespace Weknow.EventSource.Backbone.Channels.RedisProvider +namespace EventSource.Backbone.Channels.RedisProvider { /// /// Define when to claim stale (long waiting) messages from other consumers diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs similarity index 98% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs index bc13d8e0..5db9304d 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -1,6 +1,6 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { /// /// Responsible to save information to REDIS hash storage. diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/icon.png b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - + + diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs similarity index 97% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 7cfe0213..8f6353f4 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -10,9 +10,9 @@ using StackExchange.Redis; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace Weknow.EventSource.Backbone.Channels.RedisProvider +namespace EventSource.Backbone.Channels.RedisProvider { internal class RedisProducerChannel : IProducerChannelProvider { diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs similarity index 96% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs index fb5f58e9..060970bf 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs @@ -8,10 +8,10 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Channels.RedisProvider; -using Weknow.EventSource.Backbone.Private; +using EventSource.Backbone.Channels.RedisProvider; +using EventSource.Backbone.Private; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class RedisProviderExtensions { diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs similarity index 98% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 00235a55..7a131cb8 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -4,7 +4,7 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { ///

/// Responsible to save information to REDIS hash storage. diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/icon.png b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< zQ`O5*Edlaeq0vi>ja?8CX1h|mLa0-~Z`C|Ff*aOE16d!l5>(b_iPz7GBRnFF))$G} zuPmX104WR(2?*2{*49L^1RnQ4v)Vt5UGpIeRKhd_%V<4PseJ0@1$=zqMt4SSH7Pw{ zk48(0mpR6&*Mnb=6;~6C9ih$hVhDUxQKw1)f$*tx3y(K#+ zdU|&+Jr!*x^o*jGjKkiIu6h+amS%imK55w#uWR&}P{NwDv~-*P7R%#P_fPFA$s{U` zFg7g>U)Hv~wK_E6jhEwpU6%HOEG<;Q#rP*SP&|^7;pI&~0po^b?DgHL3(l^t3#VPqzU9Bv47}?V@aS`B5EL4UpVed`fXY{PWE(zb zxl5(KIPDB`fH301k<_!I3`atHkcEpX#7|V(IvQ{u#RN2L>KG>BK5@cqj6io$Y2)kI z4-Lds_4jB{BfU_FZZu&-_KIYJmNw%e#5*c3#2serpx_@TalhKC9QpfO??M)jU8rKCr_fpA*~AAQPKdLhUU-TzoVE$mE0G@H8hUt)ygK3i(>nzxsoTcvNNV8r{rS*!EBkOAeKmj#Z#8 z)qb5#x@D~YKqVzPjeIOdZjwi5N>c)RQTW|8ISM?8S#GVZIo`G}x!X2g@Iq}SRXT?1 z8F?VoRo8iKsc33)zxI(&$&!$eZhYEv>UANMT`x!zMo2&k1NhvDl0i8!v6orOVcQn> z`dCHp&>_j^=H_xj3BdJ0Rrfq2-O}7Rx`Y6_6s<#*9)$JEjPK2%WcgjC1}W~N%uF+i z*L_6&AxX2W@>W6Mn$L!aqbfI#pp{z>-n;t&|6N4wZ;N~#P>}*h( zM0|Yqo{}@>-I-*edfIBr7NJ>A4>iZuTp$DV^(5Q#Je=a{u%WS% z*vK*+S`eA0HNR({qkBSh2bdk!Z&am(@-=m%>SeLvB{Deiq-7oE?h5a_qtaj2T*HMb z?18wSMyl3DXFQ<3+_plOvK|5^sB~U(&7tJwyHH|OswbP!?xJ>A83r+>$ji>AgT&ep zjuk~zyeKKihpcm{enOxUWZ!Gte@#ac*=1*K{qPwj1JaltE6c;f1E$&i{ymRWn>Hrv z6eB~B_o(fb@`d^%S5CJ#_)1xU(*TK6@S5L3Z%R07I0zHSz#F^QjfpHR^$H@u<(poM zA;vheY+J!kseoilyw7Q+5z7Dc1(%64N*4&c7ax0Xf8Q69nfG~}X0v2;u(VNCfk2*l z!NuvyUn9T)HU(>)^5kTumX=JE{$M?bzCG#Erby&`mt-K?*N4ghX=NjAP|UXz1T`$& z+zF`Pdq^C2uB0B%NHA!-@ci>J_*IQQj=;%A%Q9f?RJ78MKl?e5Ecrr7F4XKW=v68S zuMWqY29&@8_vGclmV`RdL_seuvy!iOy=M(w;+j3`*! z-sK!9m_UU|AaC3F4wUazC106*TxSNgNZXMT*RNlO6v?~(ev&h?vSc}^GAw4Fd0F@s zo+v2_6`jCSTTs*k75rTTc=uBZW9z|aLY*pv^I3uqF-(r4V8YkY!LlGeF@Sn+)GuH_ z{Vr~V#OWbNP@<|pnWtpPp!DIJKL44Jl<^g9|1R*!c z`Bo$?B_vh6SG>m2A!q5UqRG*|xZzoPKEA_DR<=6yXr${kWlUDOBg>LC<%y`^h^7aQ zAqrHeL3)-~EJSNz!PZ90!0-X0<7Tsx?(Xi@5SLc?%`9L6s)AR6DZHGN@_Hk^6+Jd) zu+;9-9LJ5>-k>}|Vks@_v10i6)zjxYqbpExqo}JpOie{kcC<)~K{Kf*Dk&-KQoJS$07OK4Xc4aEtU2nk_? z<3xr7!mv|{wBBT@kG<*WXaZ_qvq_o(?&U02(3hZh1}5))zOfi32+ff-s#mI%RQv=1-!1_>S6sXZ%Yxbg!tqGEx1}AT3BPV0g`N%tX z)30RkA^8+_;AdG{pJo;gz;d6}1+%y3FMw2uW!V~#R_`ZYf=a`e?V%*)Y;U#u`LJt(wmz1S9(_{4d~GvZ1XkP=Nxb9$ z$#4mw<-U72ac=@gEX#TXoF9Je>^uFF`+r!v4sfdXHy#QhBuP_>I95`L6r#Z~J2Fzr zUP%#3nbAO{%ut!fo*^P5l8|IpMnn=qkwoPG`F8*JdG2$c=icsd{C?l@zMu6*z8!{{ zj{P^^cgFLpnN}R;Vz)cAWhLZJ?d|R8fpSseLwnOc)B4;}2Fv1k?Be`Twfv_CofAdI zS(MO|qwAtIx4B-Eo{f^S;9uvzd*po5#3_}V`97fqt4hnuAK`S8dit3MJrFu!hu-ps z=#26&3@Ljz;j-JeNvCAVZyGbt<_fO)@$>f#{z)P-v`(yFx9->(joFQJQw%S4LZU-l zTpu6;1h9t_&#cgR+Og?j>v^GR>2&{YhSAZ$ z^wcc@nPqD!{uw6(zwNvZ5ojXkk>a>r0&;o}cX)aN3Umfxl-yHXP!NVQ?FRWfJ6r1S z!i4wKWW)A+K(gRq0iMl0(FyhSthDjj!}V@9uyz1)5VNALC>bj@NHYGj`?)3^VwgJC zt7{mMDDvjumU}HXRj6xa4llm)xp!l?vy)h#!8R?l0V1&;GOI+iyC+yk2VQSjUbRYR z-*dFSb>0i_yum-bhM$S%9K3`fbIBW}?t=nA{w5}B;TnVV%F!`5a} zP3(GxF70!~?Qgbmw)qSG1!l9`)X49wiq@jN z>w{`uH8xtkyyY7??^O{Uu)q77PrxdX$#3~hlOScoag(O}Iorf4=saEPB=^Qlly}RX zJs0n$51Ad#n+$%Ec0GCCRT+UqY`IpsW`23MwOS6J@kb}{Tp1u6y<2p%J}5$z9wg`iY%jS~~H z&i?@AF(eJU)oIU7F80*GtC*UTlai32vi@cT$0dHF2uB$Kd+`k5voBx1JdpK?t5xoz z==dX*?|DWJx{~ogTcO6;k@?vMevb3YSA4sX>s%-mAF?6+;Yn&pqPYnGt-^Q}R6bDH z!W@P6PH*x7=nCS9L^BMz&3?+l%)Mg0+5sm}k+=hn^m~+qaG7rY`Y1Fs@sY;a{A1|K zcdct@>0x?#7w3$!by(6;UGp8-k9E!^`;V|lA>~nb^jU#!>#_%Iy~m1bOuuvf&BgNS2+0$d0KTQB zr?+T#_A=@)I++URemOt?%61oaOgz-15H{h+IZ30&m>&@4S-#2h=TRto&{xX&}v%YIsUvT-8IA}-UW_EYX-EHT@2=ak|U{a9FU{OY76O@mdH1}xfs z+u)5Wj;`To3A!eKVlQfx>hsI#VPI0APQ0;ky_kdq%&U{IF+k;P&8?c3jN9~jLlR9CsFUn#`tBzRtk2kHQVgP;Tcv^Un>I79C_jx}2l#kixDH{bnr zJ#7Bgg#wNZ7U6FzR|>!&=$xfsi}m;PWVJI(xWxEqcIh3Qdv{S|p!G-hdN!Ee_|>3o zQ-Krrwf8O&R*K&@QU-N6rzdXL4%q9pRyV(86z#}n7F#?5A^J1hoV=D5#!RI<=1?r3 zta^Pv>7g=T&@xa){mGWQ8mUyGO0OgI2GD(oPlUiq85kUfQid;xUSR!Bg)*maz>BJB z+IjZ{u6OcpZ5{}ie@@nD21V`XKQD9g!i|3)eRu8Z%T?6+dCnkREFt1)*uWVY6Vm{A zRSnKrq7U__pDT}Rd#g@4Ug{c-bgoF%P`vh99`Js6&;R!a7gS7v;Sujb{q& z&SR?vzN^pUI!&%KOt)0qmu=!?*Psro&&-8U^gt9;P0VEUW>|g#?wZAJx8r3ha9tUH z7oXgZlj?f851jhX3~UEF!EYpg>?D<*dE|_~E&H`GHw9>#fLnP=)OyHy<*F3a{{a8Y z)%^G2y2+!G?O@c*^Ni{(4P`!sw*?Df+i%WnzOj|R+MY4}(<1dy%iSZLr5SMhIu?Ph zAWjiNZvnc6>^FVZ=((w`$!fiBEqYQd1fIdQq&p>6TV7tqhCH=e}+aa`u{uV@;$_6w#@GUE_RmorPj_Y#0&w~3HD7we7rBg zFmYpk`f-U{CDopb?ZEpnxAt@tr2r|Kv5maEy!LL{|5u zXvn%4+5e-M`n_`|Okd^pdWWRlq|jA*pEhqH69t1}`l-_Xs;4KV&>4`$4L-la5a6jz ztfwr2GiGO-ytPqT&(6-G+u+r5J$uhZf={of3bYtHpn)GyW&K>$_q6(lMYx-3;Ckz# zuDYhC6dVyRUmnI0cD+YNf{E>Ck_i1Xm?03v^1$~}o@GB+v*Ph>K|3H#4W_ z)zMLkP-1Atx-a&60@d^WRI}!vxEFf$@IT|F!2m{lwtM{u0v*a#d;9kqrk0&YIH)Th z{{z(t+`ey9Q=H2!vvF}9LCRn}cQ3iOfZv1jpi-KSmS~F%*sebOBOii!^{tSav$ofe}4nyRHG1$S7T!%%9zdmeIIOM2eXPknq#dJ)Dc=Jv|+^kv1*&9 zY5$7=Qp4gLchS;N@a2n(^8Laa%c3}A_mEX^sju=9TvE`x&t&JGukLyDcfe5osnSy`p15It&v1BkHO>xd6k?aS-lTAts!#eJ;!$8kPFDN+Ho5J09=blHTLBFvI zD|p;`Xji{qF8h-&@jz&m(zGQ#Gx`)L_y7A1rWGJ81>^*Zz{8Q}d)~jJ`R>|{l{V^Z zUn#DRaBx@jHUSwwo@c&{^M83>_>kMWs4WAU1-!ncA)Eu<#`uv&T#~t z|2F{v>AMH#lAX)Y(g5s1>FUqDOI8+Umw-lVSgB1U_Z*dALj(t|AMmF3Z^Aj1n8)9) zQ4O}#5a(qBa)-BXHP%^((pWYLzy zSC(@8Y`-F5=ajap2$tsjH+zGiK_xt0*r{tES@D|zSezyNFVuR|c~M-x4Zdd+$NWWC zFTsBX%1cmgToWbxB#R#1ENt>rj2*GI*AtN73sU^9*vNmKw9kr{eIj|QXr1qL?o0b( z{T?ag2@6?8*Vd&BxkISDd?}-GRUqRKm2%?n=tu}{&9GZ(T2_|6y2i|v2Xh9eI)|Wv zh%YMILm!KLOf%Bgr#srnL|Lm*F8F?ozf+f61QJ4KGY9Ha&p*G=MX`j( zqU44842ys=?c{TsI=p@?RI{#i^3uN759C9^$QucqTr)RTSn(9zdN}o@!7Ag=x4o9fX111hD-Iotm7QH*h{OW6qpXfF11n~;1(trjP5vUbBG4?ZH$3Go3~>jl1Dnje{0|O9Gz+mCnAQx> zGgHkjXL)CG5ncj$Q}f%makf74yieCH{Hi}=9AJ6KTLOS56t=c?Y|JIgSFGa#0RZP6 zt{2uD)my9TkMbGbd16UJ_ub^bPeXwp#0D%Y&+j_k>=);toBI-)!=F(CYSWjIm}n? zbdWHduOlN>*7-XC*x&dy-O@sXAswz5{ps>kptBgarwx2@Ag?9y#H598!=?tbx33vQM3kWnP> z)474G2+qsFydCqV)wLKS+-O*f-d|QQ71|2p)@s3 z)mN=!Zly~1)Fha^mfqT9O>#hVON-}~%Z#?Ig6tHX`AxzGOFGhbcG_?cPJ9Le-Hz|)}5zNDt zdyw_V1;H2EeJf0_?l50bHK$YbHTg`>b0dlCJ&r@KcQ$qBthdfHLS>1oTvflR&9s`e z-BgBYB2L`zD2enI+`!!~8t6yzqneWN>SOe`fJ zu5O)E-90^8aAS=ihJnFt$)eRQcIM(E3tAL6U*1DFfz4Z9E= zKg8&JUJ18#tgw$2UxYXwj93lYGMbv!^1h2MF1mVpiQ7xcx39hUr+kMCc^1_N`|Pt^ zmoar}@daH|6O#En62@~L5c-QjXS?3H`wYv!+`FE^&zha_0*f2s@K2B$o8u0GJ{K^7 z{uW;6m0#-=v|@5I5G@*-2EWf8^}XmvNhm zf48w^9Wj`{UchW&6?t;#d<<_Q^PQ7a#DayQGgJ^#j&_ zAoLK(xCtn+Y+{*znYqKF_GZQ^3rOQgi{=-yp*6a(&gTzAhd+O0NSU^?&X*0H zSx$_Lu+9XdfQD!-a`MXR5AgHFi)FnWa6;{Z@)6fJRv;=pbSTgX=mnOC$CZApYWbVf zG}Pi%Bx{%fpa=k6gp^6NW6duY3;y+R<^*c|UUqheBl-=ZHvpQi%C}cs_i<57bo9@w zp6mHvu?nd3Gqs87;D-RNBhEKT1)9WWtHSNvtO)8NsaUP+!x(L{+CRJOh1Vrr_RrsL zm4#6RyL|lkv0y^{5^U1IqDWT&Nl(@xo6k0D{ilaZ{f{is^PLKVn8P1uH%Ng<^`jTK zALr+j;s+0#o|&onr=x4@_W}c%yKmxn3?L9gaZDLzhuR!ES}g816^m=fURz%X?7aGz zC)Go$=l0<3cCO;@j~K3r@;c;HP+#llD))%Ba9O+*wL>Axgmi|YzO3t z1rruJ6rmhflmxuzci;+5S2-~G``xChfqlnHscCEBIJD<00i4oxm%r9Li zJmH*xpbGI3x>+~YPhO4IguN9+4oJzbKVR<&uC#2H8iQ&?9jOh3|3hZU$He%%UFVk{iO?ukX1$lO57io^=GWm# zOY^gr^Dl%W4yVIUwiR3T7t>c=;*!&suN4*+hIeB_aBVe#U|^OpZr2t5#S79id40X= zi7A_+uIXWBRpaBv{gbmE#6phR#!9Vmhos$eAD3w?GKfq%74c*d8WrjlZut&1y7d?F z+uQ%quJ=u)Z^CJ|J`le3qbEtK*M?imE+)He!IpA@)kFqnkAe>!cH2{Hs{V6!5hrr5 z@s6wBb6eTWC>77g+|V615yAj2J}3f?sjEAcZs5ETeU85LF&gjC^{4X)Lrts-!1 zU#W9GX~PJX*?zxM_h8Ch6PPt1WPDZl!2x+i;SS;NyVY8a--SGbC_hT$(tW3^iu{-7 zhEp==+o~7-K2*4vT#!lapI+%0bFOZWd$V^`!m;=h zGj?~_Sw0v8&EtI4+W7l{(rd}7E6hTXF9))c$Uq=8s@@_2wI@ngD8xEMZaGq41|F7? zWlEvvFAbMH#m*;Tipx8FFA35E%u-_1DYIU<@@%2;@x1=UXJ(!(8$G2>{urjMbvSo! z2ngt5*5|lD;=yVgV1f7HnbZusIJau;aYCF!Mt`8nP4~Jf?+r|i=gHB1tZ&0ul5=T~ z$CsV4@^Krn!igzOLnG)^yxD@NJj=Ny`jw|cJ}*Nd7y%Z*CwW1ybOGP0 z6_DPybF`K!lh?|Hz6_|dch~#w3=;wMjkEd&>Vwwi<{jVo!Cl6Ii2`$p_OkHY*^P&W z5;heZ3Q7nrg*+AZx@GuoZ@ar6JeEl*zUo%GUzm^14e7Q)y}eyYzum&<@MV5xzXLd= zX8YGvsF|4wLMO>d(i4K#Y=1d1^DivU0Zk=|AdoETP7uj@@cH&Y{w>WK4(ub2^&&gp zPH^II#KVA_Wdly~YBv^r>&}vJT*02|^&{0zZ*PdpN%-rAD}*nZf9B3)@`lqg4fP<5 z%+r^D`8`>SWG|DK5L^M4H-hawcJ{Rm${V0OMD^gjS@=^yLc&$zM(=Vg)zUClqeL(TKSZ@l zv@4ZcZ{w;($_wHF_V=xp^sHA&E~(ONnqCyQV+ZyGo;vlNTF!UmvK)lMwW!;f9i8)K z_h@gX(|fGL4jA27eZ%hR7isRh`UAVMm~`9OuCvpVgte|;+4@#GPa~PY~SuzZ-(%(thkA`d~#AD@ur*l%eddoOmm}Q_nOs%3QN67f1 zHY8#IJjb=dM_X|4Aef3sj| z`Tfd}zZl6N{59|+*)(b=tEzq6J1;e&=$HHT-a`LVG1GILr7Xe*REomkW{P3}!$yi} z+`|qi+g{-haenm>U0@|3>ot~T)RAgmo@QPDhi}9h-n>c9&b~7}`Yxrg&!zWDblBup zQ^D(A5wP%fS4AfXXOb?MMsrqPb!_c#e39XL-E~J+y2mEJ*-pPeH)fZjd4TlZp3O6<|djs8BEEm4|C4nkwG)dsx2O=dON}f z_P+?e)~Wt&Y8D{lVPm?!)m8Dm$H_*40d_eW0%?>LVid#}MIf>D&xqK7?u(f5`voNv zbL*;I{1bZ&@tQ!u6CIAlGYuVs=ox3YH9VtA2>P_!`{>n1?+*nH0(xLW< z`uvE)!YocoH)HlgP7J-5M z{}^w%Czty96~sxXbv~W{+Las}amGPq>xzvW%xRgKmawxU!jo&u4Pb6Wv0gsco`Yoq z12wub^7XzK=6K!?2?;6Si~1PCC<@XHPD7L-h<6#ChN6eL=C#bz8>-X0zJ2{_3NJ5# zaQp8a*|b)OF|{}NV*(SK50~$<%JJ&76VvJj8tC1)Hs^d^GV!hgk59>V%Qr4SyNG(i zBY+a`M!I0Q{}tW`Awm4MGs`%Vc0RZ~k_M^v4q>m=!L@1W>HUcC7(y?X0BR7I5b&4= zRj&O9aeW;gRllMKC=zWct|iC;Hv&8(iH$I30kMMilwMfqTG3i>U6_l=ftcr7(UT8Y1Bt*%f~;qkS0u z`nB)Y&NqFi5Z5~|b{3zK;H!p@1zr*~BJLnLXirRlyDH5!2NU$0v~?>-H7-hBM@-e# zVL$^!s(~Jhm;tW4unfJ9s$Ok)@;Q-o=OZP=D&{0*Fot8-gXsm4ruAFDdVRjfb#h7f zvhEzut$1}fk4TmSj@Rp2%+v%+$k&=}k1565AGUo>RbbY6$3t$Oe4D0CNw3if)(|`z z3Vg7!mI!6Bs}sAY#*H+L2?1%-LZMB!57)u116o19jyi0gXmkXbG$O>TKl!;1IXSnq z3|pVxzdWP2k^u$|bXazGU6Z*cb>QH|HA%klPPcDHV^{PmTW_TOCV_K)=cT@jy%qMq z#PeG=`k*?$XpS9>`XDjZD)4bx4Fb7K)(A+Id$6CC>v{UXmNEr7aJ@ha!uvU?6oBD_ z7h?ZxAZBLV`d=WG52ZSzVM6!x_TxuwoSUd9$<4Sl8J4miP=QQAOO>37X` zujj-J(%TwU2bFf+M2f4HY*1V`IeoDB2qq0p5qsY4H?Rat^R#Cl;#Cd zcsHMFYW3&#mgk5lM+Ua8Qxtct+50fEii;0`sv3l!mw3=0J$hueA{~(>gv5OF zW*c9ST_~T;t?#DaK6KW>)FwZi6bc|sr$XVIXb+l4B90M?R!X>_D;-U3Qa`9Fvrw2q{i>LZ>#~p z*kx(J5(ppw%pPK=dnds5o5{wo4940?>I|L~eGy;iAR~NRY=Xt_09>)J(Gs*dF^l1>ya`5>3Uo8j-#HEJLX27m#+ z$&=rmy+6bZ-s(n;4%@S~KD`LW64sLQus?$o)*n5a+AS7Zmn7S$dz1QM5jWi%5>Tmc zeY|S{yfG?WO_Xi;#m=(TsZ%CcCOMF0BXWDW2Yc}eB{SHCpx#GC|3+?rLwoPA5TDe^ zSGWlEMiv3Wl5jVK#(qKq)kHhK&6*O`vNI>6o!;du^UiJJo?Wy#$-MT10 zwsc}_%nDaQ7y<|>D?Hz6ULUO{ej}d?{Z(-Oy%XqZX`1hNdPL4O_#q4lHf(scHzw1lXl=!m z%kG03NnuLX{AJ6P>XsU%tn2;2CEvzqBUbxBP3Zfjed&9vwH*C@kO^?%Uj%cp7XuSDxI0% znTo%_!N|codr8;$*B`%-%efUa-m)`KDwdzp)5DV1vZ>=to5{Yor}b>8wizQckN^Hn z!JVlG^DA1fs~RMhxn~?Oimj|!sd9{6uspLc4)RK+#n~o(L9A`X=;&zFxY*MF;yBzZ zq>fAQB?2@ih&a;OHq$LZcb)WJn0`O@>p)h<&!netRuiNJI)Tvq`H4TF;!Z3e?-ks$QkMM=&eeZgS$s z5ryLbb?bg;DT^M=Ln`7Mr9}_LKOY#F67cr%!u`4NW0{;?0dJhjSwj(4`tzX)Zw@Wd z4>%g0uZ>;@!T+wNnMcOAe{17aGV6I2sdAsQnV z2Yst`wm@+_Mo;vb!(a`~4PNo@4^3Xz!+35~-*fx$5sfC$2QVx;`+h7X{3bweio25f zNp=?o!wvCMZcly%v;_VIHH>u9sa%ivkHW7%m)P}^);TqmHL_v%*LBFkq7&;z=he@* z3>tN%OiN-Kbs4UV$%5;KHgL>(Q^>{_!Pfjy<1E2BqKjacplx_}^1=$~&9`Ij9OR71 z1se$a+W^uJg@+zpLBh>V{NoM;t|a!g1)Z>gK}5Q79ET$Uhl#ZrO$NLR{oE zZo`A`i2U=JiHo5^?XTx*qhH7NLuwiS$oQFR&Cug+wyQ<&|F5e}&M_BV#rP?SD%iQ(w_w|ZnvNcm) zUe%=huKyMHj<63Ztc9ERakEMTJx5MdQabMjd(r;uY`Az(;0nUG)!7-X!VNBd9wt?T zc1?O10A?E8vdM=b48h&s%5!>KhUBUP)+5oOlvg=Dr;baum7R7gc$v*nHD@Vz9D(e{PrGEr^6^}M_kVFlpyMpkOuu0Q}YlYRp8OETKiJI->ZoYCH! zAqst-%fzkfBd?+iL}u3fe0Snb-PxDp6Bi&?7qJ^#j z673m+)aH>3@g+IN5o}8<%&HeJ?!j?^b&A9rXo=2_yzT6yf*B#Tz|)^C8t%V7*tnx$ zP0PsGx`7iQJwC=L%K(Q{^xO0m@4acxM@KfTkvge%@#2h`ptz(N?v3zV2!(L@kpdWL zLil8GTM%@%dv2iC9ndj1p2>#X5H;sb@>A>^WKsK)9+{HzfHV%*Si(<|a~8(4n&s1T zCd1tthqTe7i9gcJf;?4f+219Z&G#OzyM zPhWaDcS48jE**qiYNU+Oy z`=O598Kqh9D+qQGzY%5d zE9@ORJ3EOK_r_M!`^dGFu^R!g9#|=aA&3W}g5?WV#)w27yN#Jcnfbu`@2uEbt9t); zd^GW)`<7CslSYfouX$Bo_;X*tXdre$Z4Ey4Gs|;X-gACiYOrKNdh=d8?v7?giq&my z^T%$R6&`9sM`dSg8;4;hK{W~5-7p{G-{@aZdKiXi>dkxh7={a|r?`iYb7r)0%y}qc zsLozAaPDAl0HSnX9wqsHD9+kK1Zp0mf`HBy?;Fb)jUKiEELJd)k=BIF>DaXn+7NEm zrER-b6W|hlh8JK6$u>ev&zpDf?Y$=5>!Le%%Qe)f9s(qh$!**q)A8_#VFn#LK_GNR z!(@!W+o5#+FBwmd`4oysslF#8B`AU1#J zXVDs-(RF(4R&_=)5XwH+is4K_$yrgUP-vk_OBJmCV2aes*SuTWL)*?DGn}r?c;M$^ znIXLK?4He8fx|}xJYN7nxK(4WuCCL(##G!O>(rEHev_V$URaZT6I~|R()kFD8Xu5$ zdyUG;?eu4gcAoo}K4b2#Q~or+=69Iu=M`0Za}_SFkTy^_%FBLODHZOHmlc1fx03*U zoWh(~I5f5^s{)qdF_J74@$BNOID#9X@ko-0s#ZR(j`aa8lO>=Dd$9DuAnwb573M%g zhm^%DC;>^DmdC*Q-ag#uW{`T||AegI8hR$EXoBnI-yjD>WjQv z6;=SLszN;KS2XJvaW{ZOeQejd4e2ia2R@9$#vzcqiTAyBWZ+$??^6Di!!9i-vmjU5 zY32S_x`1V!$1jC?-K}3 zlITs3reWmQzl$8;ZWCH7Dst^m%x~uBn8Y%>@X>uJP3oQ(Bb>iKh$jgf5FA0O@i8_* zyMR_%MJ_V@^t$`CuDtyfc3WddnQ{@B3nJ?T5-g&*+-)sWpA z8el4|Mg&iX|DvF=2B3u_mdNy$GNPV=@nGbtn0OklNs=k}|I~;yd5zBZiTy9_)+ulV z9w%pG>2ZL@Xd+xUSlUzX1-uT^H}!mLqN7rOrjt`yeHWvR8b#pUi5vB^80kX>MIh<| zg<2HQpdWZ*XA#*}2Gn{{m%6qiLU7nSgWLQTcfV)fdSo2!eY>Q9@k8M8@#)Lhw~6WL zY>+Ks z$Gv_g3Sfcjom`eBPGaFnGk{n18)h$p_iCVOBr{ZCTmatcjDr}dEm9<>6Lm3lGq!kD|XUyJXm)Dd)^MqQ6pl?zY^Uj4Y4zmltLb^>mZ;)n&k7`Xw z*qBA${gRPL%K3^4-sOYIakuH!3@Q@(l$2 z6N6-lqB)Q9ik#CGUVaUOCEb=X?HAqL9x=}=#TguVGJsqKj;eJVFYQ72wYnfX&@DeM zMKwrE(D6fjGWAxK3r|dbFieju=zFyEGzg>p;7dfu{K_JD|4U%adL*fa6l~(+dJ`ic zYt2O`4Si$i}&rQSshwm5lBU|c>SV`vcmfFvkKQCR?bCaGCq-O%XY zoA4M?K!x}~J0-d(9IG41JV{_d>kE992T2c43PU};Vy$>REPqf;|Es(a-wR7$Bv8w2MX`e!^R7=%g8m`A zZa#=8(uCut1(WN?s^PSJ^r4UIuzBddLJClVt=up>&A3|78v#pC- z0`2eN#G_+pyc*rKXk@UN2xX4}n!1~P{@j69d)tRyKHkHkwXz!Ah^MSGGc%LZ`^^1< z>bY|{k8;O(RrWb@cYLk1?pwzEvzejI^f#HCSa<@11rp)tT<=(XV8vfVe1sRk^YP*7 z1reyO2+{@NaoRdV>PIMBmXm`Wh_}aV!|5-Cq^OxKaEuB$95>&C+%n91Awzmdj3_Z> zpHx3K}lFX4z%gg0xqP~BRUK^Qj$j&yYS5h*K4xT znR?5k608vZ;08ia`8+_E#Kc64yN8lvU*K{^EDFgxhjI+voRS{nV*NS8$=49V6QT;U zIr{)S_hX?TR47CKsPT-(`nOG%KSUXv$`q0>3M4j*q7Qn2;fwnwUv0Gu*seEVunb1i zq|{Wk+ZLR{M-uZwcwI(W8#6bY!S(|ge*Acvi2U?9AZ^clnjiyi5)TxJKKn6*_Q*1m z=T3G3vro!3ZuoSM(p|(2qOHVMr4|(#y6y&(fc_YFAeu_CLxht;H_5zAC*0Q_NePmY zgGlx*q#ASvq~E4cUL-s`2p@;F~dPIfyi1Gv^sbwRgfs}q2Sb}He zXE6S=JnaE+r_vqQ!mUYcUHF+F1(R>vqo$hIJ#HiG@ ztH`#zskgOt0Kv5dBRwawYL0`?DMc-5R9sq{Qb^b~!l#aI2qiC!!jMXwhElK3tIjN5o??H6_l*XUH^%9p<~tu6*FciI|b{jkZk8 z^eN0fG?Hbxx{J#6Xa5sO+S_GXcy1@8(TiQFJy!S_w^Ea`ZN`w)On{5&|={NaN-3O{5v92(z?e!HuyA>8m3!eN#Z3UmZDz7MN zuPcU+Nv;h_oKB)^`7%Vi9xOMQ`nVO%&;_6qYp~JjNvgJW#2|wKC&b9G>|@T(yRqRv z4uhX(^|w~J;lt(Q#O6dq`Cx!Cy@W~yw2cI~kKT)(rpkZpaB6oW{f@ZGFjN7j+xxOB zdjR`TmPgjxN6JKneVatx2Z`$C$l$|?OBsC+_;q3epS)oS10O>_N87riWuD6F3uBB>iTZK1}o{5TL2pNBfsl;Fjr@ap|PnAUjQcDXh%gNCx+{R05E{ZHNk`Qyk z_=6rY@&f{Ewf#fT5x2V-s!=e00Qu!;JEIgXN|ET|iHb!<{N{e!O1j3ZpI==@GA8XK zo^h)bCbg#Rvh6XtmK&1e?waCI8s?nEZJy0-9u?+1ihhR=>4`dxQ!OW|5#xfjk5>Js zK<}pN82r%2Ui!K;p3igAR8Hw@%Tox>Uja2}8aA>-& zMpzUvpnmk6G4VpcAEAh09Yt6e`~!u@4%Y?2FiVnePCN4~jbLmDWoHpq-o&)q`p)M| z+#E@z_t&N>gwm*?+*KhSy;0pA)ymH=I6AlQ5}AGU_@Vw(MF>SQWRbmbhQmGhZAmCD zA$;8fPc3kQ;vAx#J{^o1kQlKRi`gv?U^&6R41sr(OvNErlxzRY^z;h0XX2j|g_d?X z$P#uDP86uP3B5bQ2)5^_MjmgGjv!`UAWWzhrq5RZB%u0W0WgXL`*wR_P?#XdUIq&w zu54CE|EUV)R8w7@`k7^Rr(2VEt5L4BbenoJ?Gy8V6+!#h)7AAS224sk4P$ExU4HFS16Forp#bSt#dT|K4Srme@kKX(+2XBAU7z9AAE>s;Wq@+h(3L)I zw*n66LR#U6@y^Uy>N@=kgOPY+EBEi@&qQ>fHDvVKKgcren3k~%H zj5~0ejp5IN*MC;Sej;T7%SLW?Xm!%qrHwD1zLJfAIz_IANb6^c5hvSEILJgH3F(pX z>C=mt(_x0x5&OS!36^<|Jte#*GJ-EI(CWL6v~1YLI@)(q<*nr(v4G&=ky;$9WLjBI z=^7~5pnY4MSX@vdA(;QgZ(XTZ?pT$yMGo#Ef=|389$`ebwwq3Z5&Swnt{tGkDoDmw zKnl<$ITxQ)=pyub&S>(7dixSLd+*T4-8sQyIJeN3AMM|XpG3xjdd!se4#FU{zjAY) z!rAX9j~~+&%395i<&QBn+j9Z(kvPgn-hbT@7+6Ova~PdVjx3Z2`xbt-LMX^o)P*s& z#}>t&&jHKDr?@K!ugyIj5|SI_116@ zt-fhn)2C8pS7_;X;*h?83l*RmYItJcY}h?JTNILK%u%(MO+q40fB6$kpmjZ@uEPpu zh{(P15^#nb0{90k>0glmkK1S9rq4zaQ2tLVU>p?o74Ch4Y=4|7uO(x2K(oTsi~@`V z0s+(o)2d!~1z}6+r3rZ(PHnuF@@%X6WzK)YShn(Wn9IiKnUk!1h+K#>5A0Sj65a6I z@0e3<6A&2S&^ycamrp$W?(dSDEzyno$yOi5>zP zEIM~m%f3R?8yUK}I;?qCypIOPlf)Hzac%6_Y~`dHqruC%6eHY9j2VyV>MBEMK<+*? z2=FTDH9D}g-9MdtQk>4b9dUnJ!?wr?K+gc%&o<5bm8kL>k%@8J@iEuqSK&S$=={7f zyGDP-9};PT}GCrKkojR%Biic%FqaYm-@7hdDpc`;7}lVcR^FCuMa>#EAJ9?<^ab@n}f*} zdXfU5!uLT$m>vVViZ0bS^Ydpe#(k6TfI*k_VH9ANFx?RgdCCLl!0666lOAWe4Zvhc zK-!9KDS?`lg^i}=Jk_1hMvZJaQ-}E-u&&8LAaV)p1sSx1x*zI;DU8QL$aFp2hArA* z8+M}x37RG6aM$xu-T;U_NJ^n8XF8-C8j!)>~5IUDhGZLvl#dU)Ge@I zg+iFdtx{I1^JpKLlnbnwMzwx@AGbF<;Ldc1TZ|Bkz^(vlHQiFf`FzDLO`c5w#RA2f zm|K>(jdj*wGpJqqmzN**GFpTtJag@y^(I+Q&2B4?Kc=9-;4^(cBp_UAS$ z+FE_*ERW{pLq_Afah^T`9jGqI8DzliI!mC-v)n=HW=jh+*NNR^(O zN(;Zw%5+KAkZt_T!zQRgtA8u)cvwJ4Ad!#R%=7W`c2WQ0RloCSmr9ZEi6QhL*B}Mt z6l>7sS&Js>5@wiaf}A4x?~J}-{(AILVzR=vNRNBeG^Al?8K?OKATYKH>@0b*;S7BY z%shvV#582pH2I3t!&E5w1f;GN@P77amvodS&JTVmXkym} z^ho&^pQOqgG*i777iQ!~GxJ}ef?3Wf?x&+J@$=F?2nW*b*x-f`mJr^fp{}7@5c{(x zKj5Isn8Q3~NM+<@e6Do-czVTq(Y0-*K3%@CqUK(3m?WSx1wRLXkY3Va9ArRwnGN?9 zHaby2U?rp~wA-hg$#37;E{4_>d|7)Kk1&^*y0nU|wq>-bdxV=eWDJM0MK;P0&+PCY z-S$`SON?OOMIS!p=pz^euW#iePG#Bt>^mb$a0ucp9TrxzU4t7JyeDb^Tvi~5^>lUP zo}8zxJCJn2j<=!SL3RpxcSqs*6Bg%x`;*gJZd~=__i5Vg07nZ+P*>c>#_@Y)_F!f#4DZxk=*M}S~g-7eCD~;Q>Wm#{L9J?*tGaaHT z{h_%-tsPnqRm%pSUU4QNM3V!VCv&XHJn-EqVxfw(E%$WUqgK1B3G0ZqZZ7!X04fs> zBX=%(L+{ zQXkWqJJ35Je#!zfiHMgM9S@_ga^U!ty`u-k!%|G#8`|2B$GnWo{A}N+rs%Qum2g~B zWY_7|PM1cJ0>_->!)?O;=NXZ?v{sJ$WWCR=Nv^OhmN15(sLrYQyBD3jB|Khe|I&n4 zc6K7^+D5oITdf01MfhBa`W%{3z`Q8rGZyB>E3GcS>ldxks65$smO@|892g7FHc3$j zXh*&syusa$MPeVw?-DU&J9u1XP>w!IPwkfGEjrfBtKoPql-7A&Zk%sW0dNzP=TK^f zF>i?Ku5#hoNzJmGd}_jKwi7P-=Y5C!tooKytX9VHMRl~d=ix*}QBFn`?Afy>r8TO> zxrd@o!(Ckpa!`r3x1bAAK$+P0WC0vo~u}p1&o9U ze+=Bza&vPtF3Sekr++}d_t~-Id3s^1lEOfH?OuUsZyI`aVF^M1^BhjEGm^p@k)}ft zp6T^gLLoqq5E8aBxwBKh%4MU+uG)_6{x_GntFRV@$wc}dv?k6d>5oeTeNXtrQkZrC zcn6Y$+Xek0<)Wiioc&a8nB`$%EVN<+=geJx#Tr!imAUh}A>oe~Nnpf?$nA?(#w!(A z{e6ObEPRjnE~h3}AcX{315>kYmFO_HeP}V+e;v8KglQluJUmJ?V>sBR_u^JZfpi@4 zCE)Z(3i*g4Tr!WsyaMYy`9eE=js;x`F-2@k*>Xe70z@Gmg?Y7jd8&_AT|;Qzdd1j@ z3Gh4H`p(q76Oq2lz4zOclGs4akuI^SPyXdTZue7{{$+ij(6(e9lPb2(0YrBfYLqs|y&RGd-x;G(rtEj8p%>eI!QWsqvFlbd- zwD#4PH*a<|15zMi1K@0g}cgM7b7fWY&aZhoKbKy%F%!f$r{TA@;YIgioZ z4VKI5d`~SY4h@xfz!CREdUIY&sLSq-B@c(kpRt&mMQLs$5e>5*5=OZ`Fwg3Yr6v|{ z&`#NT==yv6LfmuzW*C5=0)h*mO#k|EJNBFxuDmn)c@95A8UsJJ0c^Yke0~;b+YqYK zf&zEmZeZGMDP9^SIVhDMiA;B-<2u z0%Z9X`iuWlb_0lE74*PWR`LxK#=Ol?d7WOZG9SXwZs^Z_jwwhfAV_hY1kwr>H&*Sn z{6S|Dl%P8hK#qhpAuAsfny(^De7(7CkG$s{8n7$?;mB;;;+Dc}7s3|P8Lo5IImsB(u{r8eJZA(ju4^ zB%?YtDB{T|T8W$k#0@FqA#cM*l6&~ZY~|y}-mr|ivC%F|Z}aP}06*W`Dxk5=^}1%4 zI%RdsdvW?Lc0tE5+PKB$^-8Orvh}U(YjLR%Hp!0Uyq7e6c3r~$&RxtU@}3EK$Ui~> z8I~KSd>Ks>7CCXr;l2SW6}9KwCP1l)rKKk%-@LIrv|;4u&4uF<0L1pc!9heObt(St zV?tqzT1F6I=mY*V=g_caS7E9qW@P>Sf-g2!a?K2Vv9dp!!>%bVwvoj=+>$|OxnMu7 z-oiJ*r8lu6qLX7j-cg%YuPAtMwJQ7U#;_P$@|A8}%MClw)7|&~H_LBxUJfAxjK~jPVYv$nghF@Lc zVPg25X<1HFyemm}VHbSNps;9Yyo#zgmlSqN&_B{wV)}NaJ@e0}a%xp`7A@i3ISR62 z;gKFW%Pt+Eept3o$lu6QXy`H2q!>!~7dIO0OF%Bjve8yra6$A5ZWz1L*;~w2EjpYS z`U%DcwJPa!AQ#RkA@|x;+|XxD{p`-uOZ=35T#Pfb$KkQG3 zTkJonf)yMh`!G(=p|gM@N%^;Cym^t!^4`qkm70(WBJdEq5sdW84X`<&|GAA2HGl>r zb|ya`Ufo+ImJ^^H5^T^T?OI2N2-4LL0Agpco}Hd+v9vL(hWrqfCq0a|diq4Z^=)Iz zM**S;-(ek1J*1`8=p_<^BX7_GwGp5|f<}<7F{imHm<1BD5b=zg%?FL<_#<1cmLW6}qnD?^a}&}Y%o$b`H1V9YKbk2)7&fs30|ql!2EZ|T_7!{T zt5DSUq2b}zfGuWiIb?NAeErT4*Nnqk&Fp5FaJK`XKFsP1iPNB|L8j4jc*k;F^3~b# zTj7&Q<%JT{o)fKETYEeBOu)XH!!InzqT0%a5W7tVniyY@&Uf!u)tzl-WzCvKn8ajk zPW^NN>TDHOLLYwg`g0asF}icKAG-Sbhot$FFyjg@8)yik_KLc)A{s?MBixM)>;}R4 zA*bQLGi?LV;&~B;57;_RRrR|*gxz^dtvC==6VkHgaYe;xlPDw&+M5+WE;B!Bh}lzs z#U|fd1g~Vf26sN8-(kEkTi`=whv$CAa#h5NSIeH+@(2VAnc3Y-YnVLm9UKOq8V~qn zJa6FB|Lkm24<|TAqu2Ypno2L7zoUJOF@?d3gSV%gSwgAOQ0 zZsyE0&%|P}@Xc?2^Qi2;{At3lutuhZRh}f8?Ze#O&43u-DZtgh9NqhVpaM+`sw8Z{ z1Yja?90T2G${7$8EYfZJ4Df{v->>Xv25<%gc?>9APEY=Irup2)q}x{t z2#3G`c9MKSb23 zZCzLpgEoi@jjJqlAqXK0sg)vb1jQmq$U+gZSxRxClDe>|_RlsZF|ENilR(uJ>t9+m zRx`0qdt971^=3MMNsKh|K5$9%)0sP$d!Bi}@7_7*a>_3vdKItO67D9MK z@?9Hd7j=#STeW}aHD04KbRB7ShV><6wkF274LCi^PX=61np6K|tV zpl-2Xs1xV@yc3A^-s%WFLD+d~L_B}&3Nm7Xglu7`Cm@R|l=HBLz`-M|t_>Shg^^(qm|F)8^R)YhZah9ubrJW6U_ zPxCElm7g1!xdKcuGImWnf_7qC4!} z!*as96j`bPe}9F)Iji*054=vAq;0vxvRMPV68%akyJL*qA%x;abQ`d(Lci-eZII?( z>QTzusd6ZrN;ZTLUe%zlg(;Pv!@vPj6aQT1*t`NfTjM$VGikBfQN89p`dQF%YcBdf zzp4*6ozCv7@*F25(38MdKwpe;AcQbEmp7^N=g*VR=P%5{VJxiwCXUW8%HLf=Rt4Y_ z4d7kCAphK(v_hhIu)<>=m7I2InWu^3clQuN1Mni{ukcNCT!VQxurJ2=eVNz0DTMH{ zl3v*)0(=VOB+!2K(6Rzu20Q~iP4zYPhLjTbE8UG{1u7c#Jf+U*6%u|igDQW&)7NAA z_xQhAx`hxZ6bcLaVRZ}3l#KWDU|$HKh159zQG@UZ7vE}t62Qk5B-I7HtDaD$9!eK8 zukK};P + + + + + + + + + + + + + + diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs similarity index 98% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs index 9eade314..0c329393 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs @@ -3,7 +3,7 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { ///

/// Event Source connection (for IoC) diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs similarity index 80% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs index 8db35333..45ba44ab 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Connection factory diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs similarity index 91% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs index 467da1e7..a9ba7ef9 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs @@ -1,6 +1,6 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Connection factory diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs similarity index 98% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs index 659f6216..f926f05e 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs @@ -5,9 +5,9 @@ using StackExchange.Redis; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// REDIS client factory diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs similarity index 99% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs index f2361057..ff9ab3ba 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs @@ -3,7 +3,7 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source connection (for IoC) diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs similarity index 79% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs index 50c4ef97..5cbe23d9 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs @@ -1,7 +1,7 @@ -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Environment keys for REDIS's credentials diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs similarity index 92% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs index 5a4c107d..2e110af9 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Channels.RedisProvider.Common +namespace EventSource.Backbone.Channels.RedisProvider.Common { public static class RedisChannelConstants { diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs similarity index 99% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index a0ae8590..d6160a26 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -2,7 +2,7 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone.Private +namespace EventSource.Backbone.Private { /// /// Redis common provider extensions diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs similarity index 95% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs index f47b8b94..59e9bec2 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; -using Weknow.EventSource.Backbone; +using EventSource.Backbone; namespace Microsoft.Extensions.Configuration { diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs similarity index 96% rename from Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs rename to Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs index 221ef41a..fcc0fb7d 100644 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class RedisTelemetryrExtensions { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/icon.png b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 zcmeFa2UJx_v@LoNMMOkIRFohfIp-WCNX|J+Iv_dctRRvEB#8*9BqisJAPNWw2na|{ z0s;a85|s4U(QexA+xI>1yYK%u-XAjBcK1G2wQJ9+T5GPk7N(82&OBjKWOe5!mbDemZF82n3B>BJ7+tm8+P`j z(qdwy_KtRD7S>1v!hI-N)m%+={=DGU*cVaRJN~hA%AEe#&qPViOx$XD)jauu>1qHLg*^fnv&59lY3~XeV%t|hfS$bJ z_;UoBd+$k1M1y=%0}D~`!RZCjL_IYBK(q$uXdYQie|*GM&%2_Th^rE4{>k^LR1uGk zA`IJ2jHeMwj0i(&zu8`dfAV&s7aBr0mh2qbqi6)_S>zpY#0`E#ZkI;rHG~!m0^dyb z6))mFBZ5U*(@Y9cSb=Eh#64Mzz{EzdDBlU9MI7})7{0!8#T^lth`^Uv(-c^x&&Qpk z1Edm*1*#}`uLbC0GCCaB)MO%|>6E@ekI!Xv#OT3Qrbds%i);ZLrzh6mAP|qD&%xWS z@40v4Ke6a`s|xGUT@Icv;OKUu)DP} z@qCIRfK5L@^62(NQh42I3SfD=bWUSpm$A{)AcW*n+@!_P3sS$~Yl$M;9!h5Nwq(l@PXNFF2pFj^(ZlorUc`92W{Xx`&;l>+79r)N1_9`JVDIxj7ok@qC7_tK3EH?}MaJ~~&Q8I2=RNO-H+ z(>^cxG2tWn7dPxvx6j>bxnoXqF^S+ff%!!@f`a>UnHPJ*F1Dz#aI-GnpG!QQXs514 zS5D`BF1igXP9}~jj*yO7?OAqa_E`2w^>y`jwa8}%sy_J=>g=l1&rZCH&r#1#R$5dg zR5gFb|1>z;{HdLqaJsMdUI>QvOSzYdV(H3{imp%BHj|z-xme=PKxTC+=eA1#=PRnB zjzMo%x|;iY#5G}p+0P#Him}VEYnG_2$C?QrpT6&~{m@zA#2igl!n$wh82$?F6fI#U zhg=maQqL+oJ|SK!M=D1-M{==_D}N~~IkWCsjrL9ej!26PwH~#kd1U9Y1Md>i>p*;W~Zk88x;>TR=@@25OB@K2Gm%Np~joBKxO!@Q@qXY(ygU4rLLo2B9WJzN7-MKSCe%nt4bk8At$BZ{AJ;4d29Kzii?Fa z?=gn+%WcYijLeNMm{&CAzg&)~i}9)UiP#f7ekynnCx=*v*w?Al5uZl*b1hpe2alw_;Yg)T(Pnpg`|`Eegz`+c@X6FT7OU}GUqvy+ z0Q0iBE6gU$(VBS!g%ufY*;_^{7lby298;%-rUkgi*v7Q4zu|7EcwN!1_|h)rdeFF- z-<1Q?1J8qQM3z4#nmKw%fJlJuk)3T%Z?gKk!hFNX&Eml&k?==VBIiURZ|2>cd67qU zC97C1plz90MF9ygjb3VZ2`O&t^*qf_P6~a;(wmaD;VABhE2#%dc#U|I_sTPF%W5(bYt7c>TJjbPICuX%-%2VDdz~DvO%@<{8R*NUqv1WpN7Don_^cA(3 zwa3ehzTHkH6gn3YnvXwxA>o|gdzumV8Ny(@w)(`8*wSY?uc}PD%zDh`XU7;*baCRd z6J`@|Y)=gim7W^cC|%mvyf)07&_Od?bE{meDy?3ivKE=u+S$P`td_3!MeUm!*K*Hj znbE+uZTB*vSM4Upr@mp`+O0jI*5||OXzKEPW_`#V>79}-;rx*7`R(`}8~r>58;7M9 z^d9uR<5zIX2ddun5%&nHsYLeEFG}LHTzFbE$@RM?{Ig`$D%hPr%)ws@+J=YGx=J;#*JN4Eoo!5$o zXU=L?YCb67yKb}Kydbb2)2rB0^RVVYewCh8)v{+@nU~qtJYfoV=jfL@kJa-B{e3CC zf}?foo_1SJTN~r&M|~Zs_d45a2HmKa^J*1quYb9^Br+&A8r&zWyFc=NWyH_2!R^s{ z_lV>cM$oOU{pgLz(_+UW6C>MipSr!6!Xw1XJ0cjVQ!_pl zafv@32md8Ve#6<>o{yQ?&CQL;jh)HP(VUr;mzS5Bg^ihwjS*TfI(gVS8@V&uI$ilO z$e-hgBb`hfE$p2w>}*L<;~E*;xi|}wlcOGV`18km+1MXG$kyo(>;Oe(cO!ddRwfqa zf9qswa@fb-#nJlv)J#p7k=95Xq^+|P^ke* zwXylvM>{!Ra|HIN-pN@7`Pb_F!cfrs zUk7rwF#Au&L4ER{f`D|l_)of_KKaq@`=0u-)B*q+pO_=k$l1bD-eS? z47MjXDUGIut*M=x6RiOAKfC<3asC<&DQ@J96hdumMiy>HHXb$D)okz&7d;C*9}CM* zgZyauk1^!!OfAej{v3mwk(CwxQDbB0W98&yVW($dI~?Pumj4_B4x*`%v(Yb&@^jmt z#xXVFGqZEFF>)5NurV@6GTYml3o!q6^QV@F+mlbs&f3lqriBz@7hwLk+yB}N0$!yb z@ve}Qiliv1^ffUqR$eYHMm8qapNILW-M@|_ZfD|xI%gubCP*h(RHvU78LBCf6W9n()YW6f5y*m@IRU%Y6Hp3^GVw}IUB(XNQ-};j|I$!gPq5iox_Zk(U{AO zosol!gM*Qmjn|Zso731B$%5qNMjG>e-&KF_{@2X@82Znr!_YrWwyB*74FA_UaUj`Q zS$Q~(8BMs^IAPZzx#69;k&LEX#%yfH9NcDX#yo#M=%>N{!-EtZEx@A~S^w1LPi#3k z%~&~EdAJyP*iDTYSxq^N8F|^^Ih?%QoE#j+yd0)%KTZg|%fAfsA08!Tfm&&ge=GxP zZB&sCf4}?JN`JTg_C}6QNR(w0B>&U29XbnWdiW#o-9qvinV`IekO|6OAWa3B|91DU zY5lXC#eZDRUxVPs_&;_3&!6FT18Ms|vNu15`rhh4AH>Pd%-PMz5h-F0C+Js9;g3=O z-tC{m|NAKcjJ}^mYm5KRLfF~5A|3xLO92aR1PlcUcEQZi&W7}dSwq@8nUOl%ks8_C zTU(eIp%$6h)zB}1 zF=aI3LYlH7joCQaS&jdE#sAWtJM>R%V5ayuIsWud|L67`YIps3y2%YATXUqT5c8jJ z_UDcNn^w+<+t`Smh1HDF#Eg{%JQ4>xqcJxRFC!}(4;MGLnVAt6JKLY{_rJDsz${q# z*m!=g+&?V+f7`(PPfQBR>ik5le(c%*Gxo(4>1gp^u`oZ)(&SH=NWUA^|9jr~zim@_ zOt_7VOgXt3O<7n?8Cj7io65s$#Ass534Dp$jMtP0`TJ!4zrex%+bj2Lg!;#U{9m@I zKSyi-Kk$Y>sNCO4^`Ei8eeBe>!La zD47HmJ^_{g&S-@oj{Xn#ej4rjXIiK*$lB$5n8VKU{fC(q_17PLfByWReYO85eSiM^ zpMC%8s%~rHECg)k`*i+s`=?$gHRp%apb82m67_Qby!Fr4N*3-&Yb|jLkO!PlVH~{Q zAD#aB$-nm0{;4M$_s>0l`s9zEKMu-IO5^v1`EeI?Ops8S4>i=E*6c8F{NI1^*DL++ zANz2`-w!#2;#V$y!}TjD9k%%m*C7sL@ZZ1WqgLnwab;x}Bsg3@7|-*6p5@hca<;rbPn4%_^O>kx`xx%ds&ub_0; z<~Ll2Q2ffpZ@7L1rNcJA;W~ukS1x|T^(!bHw)qX$Ar!xI@f)sRLFur~Z@3Pj_?3&_ zaQzBOhi!htbqK|;T>OUXS5P`^^Bb;1D1PPQH(bAh(qWt5a2-PND;K}v`W2K8+x&*> z5Q<;9_zl;upmf;gH(ZBM{K~~|xPAqt!#2O+I)vg^E`G!HD<~bd`3=`06u)xu8?IkL z>9EajxDKKCm5bkS{R&ElZGOXb2*s~l{14&6`s-UskhbtuByRAXBe&ci)WY|bkeW!T z$RiM*ln8|XEd=800sK9WK)A3V5DSI~1mArG;)311>rD~}M7)u-xQLqj(5GZKccQ+& z17SBW)s@pnDtRIV@E}4?C>zj9w!9a zHZ-dZ`$yC;x@|Bm<(K+w?eD+stSw2?*XAmZe)v_WOQ>hL6MjZ^lASsQ=ez=A@z61_ z2?$QHT+C|lM?=Rjy2xVK;4h*DH(4;-QFpK#+29V^twOkCh#QZ(LlB7kt1&49>dxO9 z|LFd=#y`3@{H^iN&-$bB&(Hd!@z2ltmx=vXp4D(Cu!-l`%a&S?b)5PAhYHmEuAd&2 zm7ODdRVb8ZO`Sm$H>ZRd5Q|499v|y?UWq=iP>R+K?J~Oj(9+#64)b?eXE>KU281qm z41BoSRkEf#y;=#6-@n3wzMUDnCjJP+-jb?QXhLivwV~dB8J+OY8p*LCf8vH3(K`}kuM8%H z3~L>}iw3&tRss1e0X#hofve4+jgz3^Nm0gc98 zc^O)Nx`}%KWNE9nCVIv%DLGiewHl>@W7pOjda;y|p$RuIMfQ9yEiRrR6EBKg^Sj$X zR^uPnP&DMkm_ZRO^|a+F??D2>9G#jK_ZB;>N`P|cs_2~-4%2h_+1Zx-)8XuN@>DUe z+uF$K=})G1N&5H*y12M-aB#fs>*J(BFlFo};~HQ2{0$9n7H#yn|2dU56VCA@*UQxt zUR;G5m=3kEj&Tly@K!8XWEs0*xQ*FzpPxK=@_NPZz)RQ0CJ)~3%hpzj%c92Sr@>pT zrp9mXvj)C2s;;TguCOH@F1Hbr328M|+1!%En34&e);c4advta&?}bHgW|94*C>PC74$HFw}>1&-1te1yLj9zAy)Tn?=8rTdarkAb8>QO zdu@I)8h`GSt5X)*->*T0b6g?oY)orltLc-%!l?Bw;ndSY+x`RB{gTDWA6MRq$%q|5 zujj{}+dMWQ&ZwO1z1FJdv(fh^ng2p$ix>+5#ifWRQA?TfNtFltJ6}JhMXDK}6BVG& zc!=#xc%`2-_rN&}$w9o#9f(V(t?#|s^d^!0c)-8x%!{yP*$q~Kta;>HbDpTuU zZ`jx%=Q@)=jn*WzGu4FWe1S~QZ#;1g%5ChU1o z=zreX1N5^^%Y}h~q)sKwc# zq@USpHfC*{EW8vo;N9tpd6!iZ(;>*Sp|9`VyLWGUd)XNlnWLo)O9pA^=%%J4g{cUz zRRGF-{hC;Ls!M5`@C|v+-1bk6FtAEJWCjNZODig-*S{7H-frQDS74OENPtVp^S*`XyXlxssEUUl|w0CPX|o)Y8I`5SqHGcH&{MDA~829ax)J z)?BBPaOJ4ZuDU;T7f3PIrxL#Ihsn^BsN+V%&CT7kC!mzjHsd(y%vZ+TB9ZgtHp&pD zO<{}Pp~6^L`na-U4#(+!huK~r$&eg-(up8Zco;sOG~VRn*A8ozT^0f)nrGEcpP#hX z@~W#2SGKAQ#E>|?P#RO2k#Q`mmpAu)T>PUwUtv+qsa5wj^B`PSN<_*$D|W611F!$> z+ZZFZTOxb!KYg+WIB(pz@%H&{04p}QDi*%n7Z*xR-@bqUy40fgHiK_JUfif{R(<{D zf`PCOvn%*m8Quyq7km4^W_V$vCk5f=#M%tx>$i6%^V8DNH5wOTTU|CQxtP#?jRJ3S zZ)`4G{u*5vlcdII~Q@Yx; z>oFC$L`O35l-M=v86GcBPa1mqw7NQCvZH1tiS7alEClKG^{LOF3we5a!ng}=H%e>9 z(3ZI+p21F%xctl){j(G;Gk2ogD`Qpz@6~JNHp9)^tJAHmtz_rJCbxFx=jR`%rQPc6 zREWdW(beUh3_N?E$AP>(p0QU4d2XH|=TmKi}YZ zP_(SPURUG49`1|*=aZ=61Ch}-TOrzZgz<|{LuFz5HLgY_gN1|EM{m6l(btN#F)1R5 zi;JVJ*Trt;FXLz4=m&IJwB@;)IM;oq;)yXCEsS~yrqixsNV{ameXO zX)?T(F;_7Pg91$7K4M+KPI%iu(@b4@$#m^8^&}??ZhNRi8u)Ab#uL=(-dVU)?4oyE zCp;!ZN$oI=Q^^D0Qw44Z$Er}~%=c!Yza1XVS()86!QlI-_xkGT#tNLkT#3Z$+kne< zSNZmBek1nWk!0qK-8|RJE6;cGb@wqSggdJGfemi166oW=)ho@?84C&>&hh~^@&oa2*4)NThM zCvKFL-)2n|#Qs94_sBYnGE!AhFwW#I@9nlW8Am54Ft_PNMbQ;wo?)C;{pnHhvtDq7 zi$r8Do)LYRab2E)j?Q@Y4ICPhOP4OaFGil6wC6!ymwt#{dXg+2(Y{eHnc9m)vE{Up zp&`Wu5i)caw{zcY{MKSp?O&eaj)I-*aImqlG2%LGv%68?M_if>8%3)|F{`(=qJGaN zziRdpm4e*4qo=@XX_wrtS$Uq6mEtM0lKsFRILaJ)*G0_@RU*aaQKRkq+;(GRqp7{~ zrY(1ghvEv9XV&JY)#p>_16czpi)HMz)ipIs!0cpaXW#abv$3&x=sp$l>3z`^WQO9` zPLv~-B=IoS+5uxWT{(R>Eh|ehbqe=56?;^-(Cd5e_WQA!R-P(aDc!xE`q3-h@D&`+ z?icx9-kO-vq8ec5V1BdyudyDbrw4M6xuYkvXH>^v(R#0jT_@twMw5HM5hA?58o9t~ zA@v&Dn?1SR3WbX#ne*WMxQc|J&IiIY)KpW2Mx^l`#P24>v^L5p>|Uy~%8A$F(F_tAW=NC+r*T zdSAgV!7U=co#AnK>)6`)bn#U_c1#;qnN0$mf#Oy>K83fT%qW(U*;lq8r`hDPGIG zvz^H`<0}k|jOyx=b6u&5CMH)QIJA2>czK{=JRaKP~?flA0 z7I@-<0a;bmvo&rj3S@;89&rn21?vn}qOR|Lh*5N0DjRT|iPJ2$8y7P0*-+=Q9eKKJ z0>RgaV>_cb`I!xNaHFW{SmjqqxbzvgPaXr)v^^($z{}8>T~MG&SUu+4E5Ge6)Sss- zsi=55S}IV9CUATutE436wD4{eDwI-H;0+36eom0k-jpKfWe-%|x%(Q>s|5#(Qki#dAe%< z+hQUxj#H;jfqlF6b$xC<&(0?dWqyDp1(hz#UYe zp7jAD21bi>DS>{e!docS;D%zobwp(-@M04Zb~=T2K1F`XN%&^NT=S`TZ+~LguAUfCF)R-U7`S#$ z!Se?xDI^*N1`!=*FDLDvni7XK0cC)bk7hp=Lam~#{Gx7atbM4=ik6o4#p1@j)YPtv z<_{ky?^a=daJ$HYX@!5k`qttniK#1^iJ#0$cxP-E9jj<)X(uOxFV7~_?^4#))jf1< zqkZVmd=XGT2al$~e*(K69y_(l0|Y?sAy0IC{CUo%i5JH&e<<+l7zzGT+|Hz`iE(bN zTeio0vm`Zdd*xt%g}Ebq*=x7hSV#kep3;y{7< z#zJ3ge0)z|UlSZ0P#hpe6d7Q<6w%Y$D+VhG(XGJ7J5?|h)?9?JFNmDpuzr}Gwe)Lw zmS##szzPybIL3e;_SG^9{F_R-;Rjz+4<=up7B&T*6Mk7Z1*TeVH-5KV^=@%ppJhfx zMZ&jlo^aAx6Xh%y8u~P^M+_{^fW0>uny&oZcoMb~xS#d3%X@d+#yli_R!|EFcFYV& z%2fOHeKt5wjdSZ{85GRSkzOlx3Cz9@2e;oW7SwkFdPqcZaj{mVJ(;??x}Mvp%kBqi zVdW4wCy{+QnyMg3C@Y@tcC z#wJs?BCxse?h1Z>^)m0lJwe#mYC838vA}$?F-YKZ1G@M2WU#Y~%jAZq1xOt5(B0|F z2NTb-Qj8b-^A=W#{dQONT>8|(_zn#Zx3;%WLth9s5uhi$T$^bJgEAgfln7E#9Hngh z)BK5zf$NG64y=rfj1WYgE>1TtA^@9-+*(E*>y4e#ol-3ZnEtnK-&m45Fv;$SlUvp@ z&5y;S%`{7~Gvq2y;@v%xD{YzFJ2s{uD|@o6tgN)O6lGGwFS3Je*xvK)d%~6|2U1BO zE=&px+zks0WkwmF&)y*MeHyDv+T3&nsjewoJxA@rJL4kU5OECcv}4DPp=h0H_mgYz zw%r2*!A-`GGctk}7A$?i)H#j&Y@r-GiTeZ^>Q7sHJ1=mscP1Ka6RF;7(sANOSJ9r- z))GOm-34E%{*V_`Hc9>NcbST)l`Ajj0*VST>6PF_#n$G0alF@!I3YkWOyDjteo*zy%@zt6)nXAe%E(z@p_5PFc*p6u0z>IZLthh z1_eG=_wcZ+mlr>5de6}>u~#cy8R8QXOp3HVe)*_LZ3_Ky1DwI5Zmmepq zSK7*Ik(4d0Wf~tJf5Mqh8#s+!?J5R%iOc7W1C>Qv`(g~WVkQVvHKU!dfG@2I>Y8Pl>%>7< z9?*9qMk0~k!1HGNb?qp@8>An5x|5xk*R;4%48VJOd7)K)d=jQ+B_kc6lw0_L_XJvU zGYf3WGj>K1=R-`%UXjzz3=hR|Q*KF}XkJ`-J`_i}7nbjnf1c-B(I&{Fb`uWZEt>NTTa_)9=z zM$yXr>IFKGSVcud(ApE2=K_wCNRb%_`M0;Vp{xoVbXZsxiyn$kuKW=Z5e*HEVlFP6 z2m4#~RiF>Y`P2f>^yLV@rAU9eos{8LAr7XB3>`gvJw7(ZLH?I@LOQAhg*m;8PD}}5 zC|3e%@EM$7Y0$~Pd`W+JGH>@yz{9l1&1L)dd*(Z=QmOGyaPBoMZe-`Am^6n^+WGAy zdBd(j6X#?63f}83|1wW=1fisc#(ChDa45lX1F@U#mb^@fi%Ao>(Up}iZ8FetsJ%YC6G0dg& zdZ9N^MC(dar39aai>s@Ofx+`n&4hLg!(bdvz~(N{uY#{WHZe(4$2^0)T4;Cf^C{Yy z)=Q1iV0Nd#15U@PDv(`@zzY!vjg_1L8|BhK%YrR&^p?nx0RL)FGqyw^r(g>~Yk!)X zo1UJ2l1VLW^}!QAIK7 zKWiF7I-^=sW?toaeO6X5xMm-@f-~E>lS>4FoY_&fPr}9F1cF{Szp(HmH}}&ht!ifN z@cZ{qo6YalPjXFAKX4DW8!ELBgs{&^{o1u-x)ruaU~;Hn?Gz!`+i_GJLxvY($d(8x z5J&-V=2K))5Mbls1fLN#tQamDUV4)vnAEGl9|71*ZFs6D$ggZ7$us3_4Xc;F1j2iQ zVKxFo(30vpWT99G1lK7&HvWUF<_R*ofZzbxK-__oWOwCD4zdEmGGbXxFdz{)!J-`i zx+f^HYinyaY;85pUW;)@pgtYUeqUubmq%q;2-IXfspKEL z3OsQZC=A4iQ_g&S6e`G@!7q)#{s4q1&7M)WywG(?XUu*26wt#r3D3V!T3TA79+3Cg z`9sWOL(izBr#3>-w6m0fk8*OXMylpgbT?L8E(?PW?jh}1BVd>qyLNP<`$L+Xj0Y9V zSxi~l%V$Nst;3(96L{Ue$mdP8EWqF+hoqhwTAcP>PQlSC?7$Tc9lbX8jS_U;&Q?{xGrAg6y z(hW7nfTn4O=J1OzfoHGm_Z9e-Vct(8t6o94A&VCzWIwU#M^`~;XGHuZ`+c-1c{C}c z6(E=d4YZHo>;#@@E}rO{ch9G1V93V2Rs>OTF$BF}5nfHN zzJ<7ub3pGFFpZbDPci70hl7&$8Tcq>R`0U~e+!^5lIrSx=}w)z)Y}ucPFqq&6(~b~ z-N=Zz?0i4XizVrkqGUO)q?M+v_p%{Xg$2Pmgw3rp`{9Wn6EXQl>Bg1%%>_sxha^jh z>!T3&q@DyP*kcX~1t9)`SVAs|D*|=0QL3U8MWfsEbz+o0+sVHou>72Um-vzeHbR5SI!DxbEavon9`<3}Z=+GfRCVy53D+KkjZ z^>c$u+V%3_MSzV@0sW@}whsvcusS)uZotVv=`-%jRtJNrkY@)#$XQrNx9Wt7lS9a1 zaPW<{!A&x(9dg70X&u$osxlS>a<%;j-wF;KXXoZF4}c}sA)W8R>e*|SGtHwenXC*F z(`If9O@ZtJBz%c-%5!NhMX;H@AplGG{<#l-3$fcnop3=lJ$VmCe5_}b607cdWOnc_E-4G)znyNi}BVBhDYrMM(55U-hlO?uC7uCsjmC^0-8d7ir+O9b+PJZwV&- z>e9lxx={|IFjjqQ+FnOLzO_r1sfkm1^LOlMZiY*wp&a(eNNzsna>2e*00deE1*hxB z3xws;l(QixOYa`2eDdLA%0vi)u_}fc@%!G$(LE8n`@d87&wxEWFEe0WmkOP{(NW#%?B za#m$4>g7zi-MO#J4(v%l2Txb=b9|; z`dk<47{t*C-5+`4952h)Go0E~R?pMBpUbecrJbk2$Hxc!Op77DG~cS)T?d#B#5Uev zx+TA@co^YpccT>fr}ihPrD(HmF1*t#{NS~M)*h~Gi%OysYrlD`BLJZf=nKy_JjJS< zbwpcBN0mHoh&>B28|t#3Hu+{_!$k;I1!#p?=fh{^$(I+*&PKM-($k}awd}^Hvpjyr zBRl{aL++JGM@DJ66oHGuFCTNDjWQQ~G|@>EDiNlgetd^X-`mR1fpdu!a(Sr9fQkVX z1E}~uKR-YG=(D3A<9u&{#%3?`Nk+zl8GZCNs$NG+OOoK+kV!{-yDTk>P!vJ8677-1 z5njO)IO)v#XLZ`Rw6@_{qUsIH#J)EMS``sk7W&Psi%px5S&t%~#8%{K_V#I&DZ zIU$kbiGJMp*sO(gMq#0Sl%+@}MfY9?VS0({msc@&M|#hsx~7UD-!8T?kVLAgsl7UW z+54)U9W$!50BD=oJzMhVM{w5T=rxNSXJnRkd>TB`0&+_u>`>cRm<<9GRfj)}ZJ6@koy7a^gFsUYhN z6dv?ykjJ>53PY`f=p*IfQsZ<%{Nn6q&t5=u8;CSKP>DbFE z8Ks$-x9{G?K?MS3G<;w8x&1XI*2>iKTcF~BmWDQKwUKZx`vYIa@bx1fzb2)Is`f&aaULQnetyE>moH)zg<9L0>%(zxu1>e8>gdS8 zsX-+J2)V2c$7($+$ySwCv*eO3O7pcK$^nuCnP(A^fCud7aDvO21`G8%pjzeSWP@#m z)7Ge42~^?$eQ8-9tvQZdmZb$49@W()qasu5(Q0+ST<|U9#HTJ4!zKegAbgJq7C&lJ z!Msc-xD-o*fG@W7Vu(0I%%&ri4iHkVf*(`T(t6!Wotlf+vbi)It-|KDX?^p>L{whs z7hBzGf|yoQa(@0KR1Ph{!1DwxC1{CnlDH!wYiLq3_@%!yiuOz!)^#M3vVM1l83Zeo zL;^ZR`cod7RA#8IC>t=ChAdE8VWHB>b6>~^ns9_y0jteaNX~AalQ(@QJXDy}gEC8qVt6e?PI598)6PZDnZ~<365DueL5|aHs?yie8{- z1|;c~wX~$buPbE5fINp%jgG$6H@Jt>BLYLg*q-2+o*kKlk zi%Jwg0R&^wbIwb8BL2Vxlr6L`GEHIz;E9tkIxx`AKuOG@Dwfri3Gr8nagKw6J+#;CAD3p73tmf)DWUvp9xB7n0?GvZ*U)(% znx0E!ZcSPK!*SF4koAJNm@rrV$y~TDZQhNYu|xvGL(B9nV?&)MYHUj< zp;Qi%4lhAw+@9sGZv|rl@vxW5kn8*j1>Tjm=T44}T2PfiOiVm9?d!zxssI)q{e3q! zL=U2mHdIlMyzj;oJPK0NY~uUCD$y5JX#>$=tl>z&>d{ayiyce7Th(JKXX@NZKs=L6&TfM;-M;lUO zS0JK9fc?-fBB{j8lUvmiGr}bOAd<1lwHNi&! zU3G(r9Eq(3o=JGnq;J3jgLHQ&R&goF$uY0}#76EAMUBU+f2iq;eJj<2S zmCMzO?>J5dDaC=h1hg9u8v~rn1f;3X!n&b~4YK1h-ks;-;<7xw*w>AF_MQqDq`RTK z5J}Kb<}1;pQz@C5o0ou5Yn=QXO_66iK{a6z(<&}NGUs=&<@a^7qCVo~b;Los z0xru*t0p5&gWovg2x=WT9LUYBLvE=AXqNBiTbDs(&ZerX9<_D-^#1V*VsBNdLJNQK z?euDRn+nmb!nka6qKSM!B za$}=t-vj0J8~8VsN#?6NX|zAeDCvhx(&nd(+ESw`pUS_i=A!I5vOQiIVr@m=wX0w9%g;ex*N8E`gG3j~>=n0u`S z>T0hkY^12V}*8#U@)| zN*q)%Cn8&JK?MrXW>o5Y>IH_F;Svl2rgh@LE{IfzqGW3N_BL1i

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - - Weknow.EventSource.Backbone.Channels.RedisConsumerProvider - - - -

- The read by identifier chunk size. - REDIS don't have option to read direct position (it read from a position, not includes the position itself), - therefore read should start before the actual position. - - - - - Initializes a new instance. - - The redis provider promise. - The logger. - The setting. - - - - Initializes a new instance. - - The redis database. - The logger. - The setting. - - - - Initializes a new instance. - - The logger. - The configuration. - The setting. - The endpoint env key. - The password env key. - - - - Subscribe to the channel for specific metadata. - - The consumer plan. - The function. - The options. - The cancellation token. - - When completed - - - - - Subscribe to all shards under a partition. - - The database. - The consumer plan. - The function. - The options. - The cancellation token. - - When completed - - - - - Subscribe to specific shard. - - The database. - The consumer plan. - The function. - The options. - The cancellation token. - - - - Gets announcement data by id. - - The entry identifier. - The plan. - The cancellation token. - - IConsumerChannelProvider.GetAsync of [{entryId}] from [{plan.Partition}->{plan.Shard}] return nothing. - IConsumerChannelProvider.GetAsync of [{entryId}] from [{plan.Partition}->{plan.Shard}] was expecting single result but got [{entries.Length}] results - - - - Gets a data bucket. - - The plan. - The channel meta. - The meta. - Type of the storage. - - - - - Gets the keys unsafe asynchronous. - - The pattern. - The cancellation token. - - - - - Behavior of delay when empty - - - - - Gets or sets the maximum delay. - - - - - Gets or sets the next delay. - - - - - Represent specific setting of the consumer channel - - - - - Define when to claim stale (long waiting) messages from other consumers - - - - - Gets or sets the resilience policy. - - - - - Behavior of delay when empty - - - - - Performs an implicit conversion. - - The policy. - - The result of the conversion. - - - - - Define when to claim stale (long waiting) messages from other consumers - - - - - Initializes a new instance. - - The policy. - - - - Initializes a new instance. - - The on break. - The on reset. - The on half open. - The on retry. - - - - Gets or sets the batch reading policy. - - - - - Performs an implicit conversion. - - The policy. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The instance. - - The result of the conversion. - - - - - Define when to claim stale (long waiting) messages from other consumers - - - - - Empty batch count define number of empty fetching cycle in a row - which will trigger operation of trying to get stale messages from other consumers. - - - - - The minimum message idle time to allow the reassignment of the message(s). - - - - - Responsible to save information to REDIS hash storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Initializes a new instance. - - The database task. - - - - Load the bucket information. - - The meta fetch provider. - The current bucket (previous item in the chain). - The type of the storage. - The get property. - The cancellation. - - Either Segments or Interceptions. - - - - - - Uses REDIS consumer channel. - - The builder. - The setting. - The redis configuration. - The endpoint env key. - The password env key. - - - - - Uses REDIS consumer channel. - - The builder. - The setting. - The endpoint env key. - The password env key. - - - - - Uses REDIS consumer channel. - - The builder. - The redis client. - The setting. - - - - - Uses REDIS consumer channel. - - The builder. - The redis client. - The setting. - - - - - Uses REDIS consumer channel. - - The builder. - The service provider. - The setting. - - redisClient - - - diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/icon.png b/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisConsumerProvider/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 - - - Weknow.EventSource.Backbone.Channels.RedisProducerProvider - - - - - Initializes a new instance. - - The redis database promise. - The logger. - The resilience policy for retry. - - - - Initializes a new instance. - - The redis database promise. - The logger. - The resilience policy for retry. - - - - Initializes a new instance. - - The redis database. - The logger. - The resilience policy for retry. - - - - Initializes a new instance. - - The logger. - The configuration. - The resilience policy for retry. - The endpoint env key. - The password env key. - - - - Gets the database. - - The redis. - - - - - Sends raw announcement. - - The raw announcement data. - The storage strategy. - - Return the message id - - - - - Responsible to save information to REDIS hash storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Initializes a new instance. - - The database task. - The logger. - - - - Saves the bucket information. - - The identifier. - Either Segments or Interceptions. - The type. - The metadata. - The cancellation. - - Array of metadata entries which can be used by the consumer side storage strategy, in order to fetch the data. - - - - - Adds the event producer telemetry source (will result in tracing the producer). - - The builder. - - - - - Uses REDIS producer channel. - - The builder. - The configuration. - The resilience policy. - The endpoint env key. - The password env key. - - - - - - Uses REDIS producer channel. - - The builder. - The redis database. - The resilience policy. - - - - - - Uses REDIS producer channel. - - The builder. - The redis database. - The resilience policy. - - - - - - Uses REDIS producer channel. - - The builder. - The service provider. - The resilience policy. - - - - diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/icon.png b/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProducerProvider/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 - - - - - - - - - - - - - - diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Weknow.EventSource.Backbone.Channels.RedisProvider.Common.xml b/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Weknow.EventSource.Backbone.Channels.RedisProvider.Common.xml deleted file mode 100644 index f50410df..00000000 --- a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/Weknow.EventSource.Backbone.Channels.RedisProvider.Common.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - Weknow.EventSource.Backbone.Channels.RedisProvider.Common - - - - - REDIS client factory - - - - - Blocking Create REDIS client. - Exist only for code which don't support async (like ASP.NET setup (AddSingleton)) - - The configuration. - The endpoint key. - The password key. - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Blocking Create REDIS client. - Exist only for code which don't support async (like ASP.NET setup (AddSingleton)) - - The logger. - The configuration. - The endpoint key. - The password key. - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Create REDIS client. - - The configuration. - The endpoint key. - The password key. - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Create REDIS client. - - The logger. - The configuration. - The endpoint key. - The password key. - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Create REDIS client. - - The logger. - The number of retries. - The configuration. - The endpoint key. - The password key. - - - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Create REDIS database client. - - The configuration. - The endpoint key. - The password key. - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Create REDIS database client. - - The logger. - The configuration. - The endpoint key. - The password key. - - Fail to establish REDIS connection - Fail to establish REDIS connection - - - - Creates the consumer group if not exists asynchronous. - - The database. - The event source key. - The consumer group. - The logger. - - - - - Adds standard open-telemetry tags (for redis). - - The meta. - The activity. - - - diff --git a/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/icon.png b/Channels/REDIS/Weknow.EventSource.Backbone.Channels.RedisProvider.Common/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 + + + + + + + + + + + + + diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs similarity index 96% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs index f5c16e98..70f3eb81 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs @@ -3,12 +3,12 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Channels; +using EventSource.Backbone.Channels; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.EventSourceConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible to load information from S3 storage. diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs similarity index 90% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 02e8fc18..8b4d2274 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -1,11 +1,11 @@  using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Channels; +using EventSource.Backbone.Building; +using EventSource.Backbone.Channels; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Extension methods for S3 storage strategy. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png b/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 + + + + + + + + diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs similarity index 96% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs index 58dd3df1..7b93a7f7 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs @@ -3,12 +3,12 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Channels; +using EventSource.Backbone.Channels; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.EventSourceConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { ///

/// Responsible to save information to S3 storage. diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs similarity index 95% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index 25c77337..db5a4aa2 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -1,10 +1,10 @@  using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Channels; +using EventSource.Backbone.Channels; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Extension methods for S3 storage strategy. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 /// Response structure diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs similarity index 75% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs index 2e101a17..c90260ee 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { ///

/// Constants diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj new file mode 100644 index 00000000..b05f5a1e --- /dev/null +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs similarity index 98% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs index 1df734dc..c8254432 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; using System.Text.Json; -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { /// /// The S3 repository contract. diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs similarity index 83% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs index 8da82527..86476282 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { public interface IS3RepositoryFactory { diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs similarity index 91% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs index 02fd4e83..06b7debe 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Environment convention's options diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs similarity index 96% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs index 47156c18..d5b78e76 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// S3 provider options diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs similarity index 99% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index 16012089..bd2ec6c0 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -7,9 +7,9 @@ using Microsoft.Extensions.Logging; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.EventSourceConstants; -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { /// diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs similarity index 98% rename from Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs rename to Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index 6ce7c72d..78f27eda 100644 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; -namespace Weknow.EventSource.Backbone.Channels +namespace EventSource.Backbone.Channels { /// /// Abstract S3 operations diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png b/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - - - - - - - - - - - - diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProviderr.xml b/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProviderr.xml deleted file mode 100644 index f28cfed5..00000000 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProviderr.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider - - - -

- Responsible to load information from S3 storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Initializes a new instance. - - S3 repository. - Use S3Factory in order to create it (will create one if missing). - S3Factory will read credentials from the following environment variables: "S3_ACCESS_KEY", "S3_SECRET", "S3_REGION". - - - - Initializes a new instance. - - The logger. - The bucket. - The base path. - The repository's factory. - - - - Load the bucket information. - - The meta fetch provider. - The current bucket (previous item in the chain). - The type of the storage. - The get property. - The cancellation. - - Either Segments or Interceptions. - - - - - Extension methods for S3 storage strategy. - - - - - Adds the S3 storage strategy. - - The builder. - The bucket. - The base path. - Type of the target. - - - - diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png b/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 - - - - - - - - diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider.xml b/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider.xml deleted file mode 100644 index cb908e12..00000000 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider - - - - - Responsible to save information to S3 storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Initializes a new instance. - - S3 repository. - Use S3Factory in order to create it (will create one if missing). - S3Factory will read credentials from the following environment variables: "S3_ACCESS_KEY", "S3_SECRET", "S3_REGION". - - - - Initializes a new instance. - - The logger. - The bucket. - The base path. - The repository's factory. - - - - Saves the bucket information. - - The identifier. - Either Segments or Interceptions. - The type. - The meta. - The cancellation. - - Array of metadata entries which can be used by the consumer side storage strategy, in order to fetch the data. - - - - - Extension methods for S3 storage strategy. - - - - - Adds the S3 storage strategy. - - The builder. - The bucket. - The base path. - Type of the target. - The filter of which keys in the bucket will be store into this storage. - - - - diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png b/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 zcmcInX;c$e8%7Z#2qKEeP7GAal6`^1^ydax@nT7KmAx!LkYrTEJsrwo*9+j>H8D5qLyP zA>ZhYeqeMcNatbJ{RhodFc1O}BnQw6kx(pSC|H<}ybNTloW^6&A0hHk7RE_w5FNnr zM!UdL2u;P&upp5_McdPHL>iewr`w@P1fm0;;D9HRu|yh!LST?c=-CT{Sd;Sj3?H`J ztS#h?g$a?%B@8@1GBOeuNyfp_U_6mdr{f7EJc)!w5Lj81SPm$#VwuGp0~?ZoQh`J+ zfW>GfBfy2j2Kr%R73PP?CkXUXpXN(8V@0Em0g&#fTfp|y=i4d3!5k{OhB?*D$uq*`r z2S4VQ=e?0IT%-^nhoycnESyV}_Z$n_#YLGII{Hfv01CuPYHOve*_;Rrh-3zZVoM+~2!u~i4pIy}Kn{E!80HE1QJ(|la2TFqnH&&}-rEx;HSSkXLViAadUBGs&RpZ zu#^MykeHMII`8S?;w^>w0wJ;>^Ko}Zd%C*Nh;$kaOTrPAwU5X^-1QL1kVHq#o+e)? zY<46Rpg)!<0|1pLgM|T=B?0j;v(tjV3G?SU^ocey1VTvvrAj`*WH4VI2}mKQU?e4< zSBm(5@<0Ye{JZ|oE0h1X{@DP7A%Hj-LK-d}qx1-`Y)zliga6;meO&uX8=He8y-_)w zZ_3Ead|QXa2(=Vx+9W%@VB}kH*OTq!r}**hNzOq%SG~J?{F|DZSY>PN$}AMBmbFQ% zb+z}S=ut#7rfk^ugn`CzcSgc+e|?At<>GMV(}dwl4ZQk_UzV{ci>s=R-CkODXF%St zH6yA2DL&#xV8d@X^SX7%>)scPKYBF!^{wcfCqc&?epfS5UAk~8J~&PgWMbe|qaUvp zq(8A@LUqE_AunO5$E`olt^Pq(YNcVUiDKoGwU%|Ex3_g_-CrHy@l64NggWN%(?YAb ztMTn;yUWz=WV^J)MXO_+yW z+D#!;Prv$BuJ%>gvsaR(3T&Om`K2DY(>huodPD^pnfj&e`@|?=)($u8FBSP*U8ecV zqx*E1h}YaaHAd&rJB`(+dpN6%sm1jM7Yx5sTYq-_0vo#ooL03eU%srobt}69b#MQq zzc{Qq$au*UetJxZk)Jg?<1T7?L&syMiP0OekEh7VBfatHLwd&UPpaEzd}9N?{MKgE z>Ai%(8UsbxEoPS)%F?g{{~~#$g}bV!{c5>}_ct2U^7=wU>5kiUz`y!hN9^QbX0>s` z@gmWQ;ZA*ja-bI`*&r#bw6M}<)f(fI1s<{(kT7Z_FN@MMG}Tm-LpBXJyisF?ZH|Se zABaBB3x91MY+Lty!Hu1H39_=H0HZpftHH2G5LKCNghs6+`Tw~)E-Gx|>}$7-IeAL;`vd!qUu51PR~vQ&q}zn+2ti$P zjF-uPki5|1>-0l+N>te=O^u-e+zRId=iBd(woH}mx=Nsau(E0)UfO0A9<#accN(Z0 z?3-nrJnSWH#ctTW(apA5_ep4X$Wgaz4Oa_uL$(HQug}ii9ax^?y6wfKHJcI{JzX1x zB@a_?ZQh>MaE|yhov~+U-u5z8M_||5j;5FgzG=sA`iHxhF3;4j!PGeEMDEDYXcZp_ z@yRRynX+dlV=TwEU}j)u1Yh_J=gXAGH9qW}5=(`xEB#B}Eov2e9mt%b4qU()x4$VV zOKGyGimonVPc^PQvF;B>x|ZX)(7nNwfzu?@>Y6QG>2O1Hk59^A_U=B-T$=sigI@&L zYG*bXMXo=&RA4gjemu6f=rzV{+Z6$6i&O5%HMjIXye?mwagJGXYSH;C-IHfLdM~B6 zWShH%w2cu|E2;jYm}vo_o)xH!rws^f)ao#ksWivgY!fvkiF+z*^VHSsmR- z{Uyun;^Gq;{&w88eNRu@=x=td*h9gskmHYUe{x2grcKFaX>s_Ui3d^tJGXkA6+@IN;s|8DzA8(9W zFX)vwCPi}2u{|Sva&6P{&rD!>b=b*QwdW3Q$+wVjP+j#Mwk%t&*%9bMhVv$W_im&V zANu+a6ORSlt?D_YdjGq9uNMAf$voC<#G0=1&PuR3l%1D#xOa69p@BGQ7_d{nV(FV& zld+KWU*Ux@M{2u0r}_?h9B#>fm$-x2R+(S0_Q9jf{NerN4XfI&Y^klct4K5|I$f~# z;Um-QWiEbEmdaHH*X=$$zY_3jqLgX)xH`L&zVn5{t&)a`;o*teyn6}0 zT(v6^nr(-Udk?)c(vj>E$ws&AO1-|=WLqod^2?LwS~AyZblq#4R9hWgVzcSUyCK{c zH%E+hBqf1XAD+A2yOO%S++V*`aapc&NLqHY=B38j$EgFW2Jn1I)7Q<`sZ;m88`;xz zy330-=<33;*EHAhJ7=YWe#}r$?v4|CbC_L=hjeij<$?KD3=_jDg(5ag>T>aZdBV98 zih;OW|2nF-|IJszr*%!!LERztLz+W&#fM(L9$Dd7l-Q~=P!hX)#%ADlkCkbnJ - - - - - - - - - - - - - diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common.xml b/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common.xml deleted file mode 100644 index 8584bf01..00000000 --- a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common.xml +++ /dev/null @@ -1,265 +0,0 @@ - - - - Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common - - - - - Response structure - - - - - Prevents a default instance of the class from being created. - - - - - Create request instance. - - The blob key. - The partition. - The e tag. - The content version. - Name of the file. - - - - Gets or sets the key. - - - - - Gets or sets the partition. - - - - - Gets or sets the file name. - - - - - Gets or sets the eTag. - - - - - Gets or sets the contentVersion. - - - - - Determines whether the specified object is equal to the current object. - - The object to compare with the current object. - - if the specified object is equal to the current object; otherwise, . - - - - - Indicates whether the current object is equal to another object of the same type. - - An object to compare with this object. - - if the current object is equal to the parameter; otherwise, . - - - - - Returns a hash code for this instance. - - - A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - - - - - Implements the operator ==. - - The left. - The right. - - The result of the operator. - - - - - Implements the operator !=. - - The left. - The right. - - The result of the operator. - - - - - Constants - - - - - Abstract S3 operations - - - - - Initializes a new instance. - - S3 client. - The logger. - The s3 bucket. - The base path within the bucket. - - - - Adds the reference to the repository. - This reference will prevent disposal until having no active references. - - - - - Get content. - - The identifier which is the S3 key. - The cancellation. - - - - - - Get content. - - The identifier which is the S3 key. - The cancellation. - - - - - - Get content. - - - The identifier which is the S3 key. - The cancellation. - - Failed to deserialize industries - - - - - Get content. - - The identifier which is the S3 key. - The cancellation. - - - - Failed to get blob [{res.HttpStatusCode}] - - - - Saves content. - - The data. - The identifier of the resource. - The metadata. - The tags. - The cancellation. - - Failed to save blob [{res.HttpStatusCode}] - - - - Saves content. - - The data. - The identifier of the resource. - The metadata. - The tags. - Type of the media. - The cancellation. - - Failed to save blob [{res.HttpStatusCode}] - - - - Saves content. - - The data. - The identifier of the resource. - The metadata. - The tags. - Type of the media. - The cancellation. - - Failed to save blob [{res.HttpStatusCode}] - Failed to save blob [{res.HttpStatusCode}] - - - - Gets s3 key. - - The identifier. - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - - - - Releases unmanaged and - optionally - managed resources. - - true to release both managed and unmanaged resources; false to release only unmanaged resources. - - - - Finalizes an instance of the class. - - - - - Abstract S3 operations - - - - - Creates the specified logger. - - The logger. - - - - - Initializes a new instance. - - The logger. - - - - Initializes a new instance. - - The logger. - - - - Get repository instance. - - The bucket. - The base path. - - - - - Creates repository. - - The props. - - - - diff --git a/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png b/Channels/S3/Weknow.EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 /// Acknowledge context @@ -49,7 +49,7 @@ public static IAsyncDisposable Set(IAck ack) /// /// Empty implementation /// - /// + /// private readonly struct NOP : IAck { public static readonly IAck Default = new NOP(); diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs similarity index 91% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs index 90700101..c2c3aba8 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public enum AckBehavior { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs similarity index 98% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs index e57c99ff..f846291b 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Preform acknowledge (which should prevent the diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs similarity index 94% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs index 0566a92a..9c04e80b 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Preform acknowledge (which should prevent the diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs new file mode 100644 index 00000000..1e983d1e --- /dev/null +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + + +[assembly: InternalsVisibleToAttribute("EventSource.Backbone.Consumers")] + diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs index 83a4dea5..61418a99 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs @@ -1,6 +1,6 @@ using System.Collections; -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs similarity index 98% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs index 6f4ac883..2d3c177c 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs similarity index 89% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs index d11395a6..4461eb69 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable configuration. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs similarity index 89% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs index b59ebcd9..c2e84406 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs @@ -2,9 +2,9 @@ using Polly; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs similarity index 93% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs index 2f429a6d..469681e8 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Responsible to load information from storage. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs similarity index 95% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs index 550bf486..dc64fa24 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging; -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable configuration. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs similarity index 98% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs index 23e5ec30..1a60fdb1 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs similarity index 88% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs index 5e39ab94..8390bd08 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs similarity index 95% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs index e035855e..194c5ff4 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs similarity index 91% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs index 5984d02c..e089f064 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs index 0c4796f2..d7a42125 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs similarity index 77% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs index 82f11784..30a85a57 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs similarity index 96% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs index 7f3d1a73..46e8d25f 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs similarity index 85% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs index d390fccc..b3e5063f 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs similarity index 91% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs index 446cc24f..6b4bba83 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs similarity index 94% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs index 804563db..ad1452d7 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs similarity index 81% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs index 0c506630..881ced4c 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs similarity index 93% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs index 07d40ad0..c243a5ed 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs similarity index 84% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs index cb1df579..694b8aab 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs similarity index 85% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs index 02e09ec0..b95a9dbc 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source Consumer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs similarity index 73% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs index 2d894a60..65bb6a93 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public interface IConsumerLifetime : IConsumerSubscribtionHubBuilder, IAsyncDisposable { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs similarity index 94% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs index a0315913..add5ff9c 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs @@ -1,8 +1,8 @@ using System.Collections.Immutable; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs similarity index 98% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs index 79d7dd36..7a06ac32 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs @@ -4,7 +4,7 @@ using Polly; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs similarity index 90% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs index 9bb697ca..7ef1fee0 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs @@ -2,9 +2,9 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Building phase of the plan diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs similarity index 96% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs index 3d0cc2d7..28ff8b3d 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs @@ -1,6 +1,6 @@ // TODO: [bnaya 2021-02] use Record -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Define when to claim stale (long waiting) messages from other consumers diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs similarity index 98% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs index 4a1f4b7b..7c35e1f0 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Represent metadata of message (command / event) metadata of diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs similarity index 98% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs index 003e50b6..86c14dd1 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Enums; +using EventSource.Backbone.Enums; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public record ConsumerOptions : EventSourceOptions diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs similarity index 95% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs index 6d217e8f..04f34e8a 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Enums +namespace EventSource.Backbone.Enums { /// /// Collaborate behavior of multi consumers registered via common subscription object. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs similarity index 95% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs index e8866f6d..eb0e282e 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Enums +namespace EventSource.Backbone.Enums { /// /// Gets or sets the partial behavior diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj b/Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj new file mode 100644 index 00000000..6869da0b --- /dev/null +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj @@ -0,0 +1,16 @@ + + + + + EventSource.Backbone + + + + + + + + + + + diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs similarity index 96% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs index 4c1e92b9..4f6e180e 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Base class for the consumer's code generator diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs similarity index 93% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs index a0e3a79a..3089a39f 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible of translating announcement's parameter into object diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs similarity index 94% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs index 3647fbc4..2a693b7f 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible of translating announcement's parameter into object diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs similarity index 93% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs index 92638669..f2843e7b 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Subscription Bridge convention diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs similarity index 96% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs index 82c264c6..8065661b 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Bridge segmentation diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs similarity index 87% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs index fd941934..8ee8d2aa 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs @@ -1,11 +1,11 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Consumer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IConsumerAsyncInterceptor : IInterceptorName { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs similarity index 86% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs index ba1f9cd9..9cc73f37 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs @@ -1,11 +1,11 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Consumer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IConsumerInterceptor : IInterceptorName { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs index 05509104..8297681f 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Channel provider responsible for passing the actual message diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs similarity index 96% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs index 5aaec665..245568c6 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible to load information from storage. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs index b488ad10..9db25079 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Bridge segmentation diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs index 6e5362b0..9db2a905 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible of building instance from segmented data. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs index f005cb13..738d6a2b 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible of building instance from segmented data. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs rename to Consumers/EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs index 4b4296f2..c112c214 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs @@ -3,7 +3,7 @@ using OpenTelemetry; using OpenTelemetry.Context.Propagation; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class TelemetryrExtensions { diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/icon.png b/Consumers/EventSource.Backbone.Consumers.Contracts/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 +using Handler = System.Func>; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { ///

diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBase.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBase.cs rename to Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.cs index 707332e0..489bb1f0 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBase.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Base class for the consumer's code generator diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs similarity index 99% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs rename to Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs index 43dee576..2d8236e1 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs @@ -2,9 +2,9 @@ using System.Runtime.CompilerServices; using System.Text.Json; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public partial class ConsumerBuilder { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs similarity index 97% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs rename to Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs index 0800e4f7..00fc0302 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs @@ -1,9 +1,9 @@ using System.Diagnostics; using System.Text.Json; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public partial class ConsumerBuilder { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs similarity index 99% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs rename to Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs index 79fb9791..84aa003e 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -7,11 +7,11 @@ using Polly; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.EventSourceConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source consumer builder. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs similarity index 99% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs rename to Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs index a60ed01f..a9954314 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -5,10 +5,10 @@ using Polly; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Private; +using EventSource.Backbone.Building; +using EventSource.Backbone.Private; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs b/Consumers/EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs similarity index 96% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs rename to Consumers/EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs index d6a45b45..3d87d912 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Wrap Channel Storage with key filtering of the bucket. diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs b/Consumers/EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs similarity index 94% rename from Consumers/Weknow.EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs rename to Consumers/EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs index 20bbf8cc..86215127 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs +++ b/Consumers/EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs @@ -1,9 +1,9 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible of converting raw segment (parameter) into object, i.e. deserialize a segment /// - /// + /// public class ConsumerDefaultSegmentationStrategy : IConsumerAsyncSegmentationStrategy { diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/Weknow.EventSource.Backbone.Consumers.csproj b/Consumers/EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj similarity index 61% rename from Consumers/Weknow.EventSource.Backbone.Consumers/Weknow.EventSource.Backbone.Consumers.csproj rename to Consumers/EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj index 9e899915..374fbf69 100644 --- a/Consumers/Weknow.EventSource.Backbone.Consumers/Weknow.EventSource.Backbone.Consumers.csproj +++ b/Consumers/EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj @@ -1,9 +1,9 @@  - - - + + + diff --git a/Consumers/EventSource.Backbone.Consumers/icon.png b/Consumers/EventSource.Backbone.Consumers/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - - - Weknow.EventSource.Backbone - - - - - - - - - - - diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Weknow.EventSource.Backbone.Consumers.Contracts.xml b/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Weknow.EventSource.Backbone.Consumers.Contracts.xml deleted file mode 100644 index 21ee48cd..00000000 --- a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/Weknow.EventSource.Backbone.Consumers.Contracts.xml +++ /dev/null @@ -1,982 +0,0 @@ - - - - Weknow.EventSource.Backbone.Consumers.Contracts - - - -

- Acknowledge context - - - - - - Initializes the class. - - - - - Sets ack. - - - - - - - Gets the current. - - - - - Empty implementation - - - - - - Preform acknowledge (which should prevent the - message from process again by the consumer) - - - - - - Cancel acknowledge (will happen on error in order to avoid ack on succeed) - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. - - - A task that represents the asynchronous dispose operation. - - - - - Disposable scope - - - - - - Initializes a new instance. - - The ack. - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. - - - A task that represents the asynchronous dispose operation. - - - - - Automatic acknowledge when execute without exception. - - - - - Automatic acknowledge when execute complete (whether it succeed or having exception, like finaly) . - - - - - Ignored expected to be handle elsewhere. - - - - - Preform acknowledge (which should prevent the - message from process again by the consumer) - - - - - - Initializes a new instance. - - The ack. - The cancel. - The behavior. - The logger. - - - - Preform acknowledge (which should prevent the - message from process again by the consumer) - - - - - - Cancel acknowledge (will happen on error in order to avoid ack on succeed) - - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. - - - A task that represents the asynchronous dispose operation. - - - - - - Preform acknowledge (which should prevent the - message from process again by the consumer) - - - - - - Preform acknowledge (which should prevent the - message from process again by the consumer) - - - - - Cancel acknowledge (will happen on error in order to avoid ack on succeed) - - - - - - Event Source producer builder. - - - - - Include the environment as prefix of the stream key. - for example: production:partition-name:shard-name - - The environment. - - - - - Event Source producer builder. - - - - - Register raw interceptor. - Intercept the consumer side execution before de-serialization. - - - Specify the event source shard. - Shard is a unique source name - which used for direct message channeling (routing). - - - - - Register tag's channels, enable the consumer - to get data from multiple sources (shards). - For example: assuming that each order flow is written to - unique source (shard). - Register to ORDER tag will route all shards which holding - messages with ORDER tag to the consume. - - - - - - - Register tag's channels, enable the consumer - to get data from multiple sources (shards). - For example: assuming that each order flow is written to - unique source (shard). - Register to ORDER tag will route all shards which holding - messages with ORDER tag to the consume. - - - - - - - Event Source producer builder. - - - - - Withes the cancellation token. - - The cancellation. - - - - - Register raw interceptor. - Intercept the consumer side execution before de-serialization. - - The interceptor data as the interceptor defined in the producer stage. - - - - - Register raw interceptor. - Intercept the consumer side execution before de-serialization. - - The interceptor data as the interceptor defined in the producer stage. - - - - - Responsible of building instance from segmented data. - Segmented data is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - The segmentation strategy. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Responsible of building instance from segmented data. - Segmented data is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - The segmentation strategy. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Enable configuration. - - - - - Tune configuration. - - The options strategy. - - - - - Event Source producer builder. - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - The partition key. - - - - - Event Source producer builder. - - - - - Shard key represent physical sequence. - On the consumer side shard is optional - for listening on a physical source rather on the entire partition. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - The shard key. - - - - - Event Source producer builder. - - - - - Build receiver (on demand data query). - - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - - Remove subscription. - keeping the disposable will prevent the consumer to be collected - by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - Remove subscription. - keeping the disposable will prevent the consumer to be collected - by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - Remove subscription. - keeping the disposable will prevent the consumer to be collected - by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - - Remove subscription. - keeping the disposable will prevent the consumer to be collected - by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - Remove subscription. - keeping the disposable will prevent the consumer to be collected - by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - Remove subscription. - keeping the disposable will prevent the consumer to be collected - by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - - - - - Bridge segmentation - - - - - Initializes a new instance. - - The synchronize. - - - - Unique name which represent the correlation - between the consumer and consumer interceptor. - It's recommended to use URL format. - - - - - Bridge segmentation - - - - - Initializes a new instance. - - The synchronize. - - - - Unclassify segmented data into an instance. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - The segments which was collect so far. - It start as Empty and flow though all the registered segmentation strategies. - The operation's key which represent the method call at the - producer proxy. - This way you can segment same type into different slot. - Name of the argument. - The options. - - Materialization of the segments. - - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Event Source producer builder. - - - - - Attach logger. - - The logger. - - - - - Set resilience policy - - The policy. - - - - - Receive data (on demand data query). - - - - - Gets data by id. - - The entry identifier. - The cancellation token. - - - - - Gets data by id. - - The entry identifier. - The cancellation token. - - - - - Responsible to load information from storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Determines whether is of the right target type. - - The type. - - - - Enable configuration. - - - - - Adds the storage strategy (Segment / Interceptions). - Will use default storage (REDIS Hash) when empty. - When adding more than one it will to all, act as a fall-back (first win, can use for caching). - It important the consumer's storage will be in sync with this setting. - - Storage strategy provider. - Type of the target. - - - - - Event Source Consumer builder. - - - - - Choose the communication channel provider. - - The channel provider. - - - - - Represent the consuming completion.. - - - - - The actual concrete plan - - - - - Gets a communication channel provider factory. - - - - - Gets the storage strategies. - - - - - Common plan properties - - - - - Environment (part of the stream key). - - - The partition. - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - - The partition. - - - - - Shard key represent physical sequence. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - - - - Gets the consumer group. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - - - - - Optional Name of the consumer. - Can use for observability. - - - - - Gets the cancellation token. - - - - - Consumer interceptors (Timing: after serialization). - - - The interceptors. - - - - - Gets the logger. - - - - - Gets the configuration. - - - - - Segmentation responsible of splitting an instance into segments. - Segments is how the Consumer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Attach the shard. - - The shard. - - - - - Gets or sets the invocation resilience policy. - - - - - Metadata extensions - - - - - Gets the partition:shard as key. - - - - - Building phase of the plan - - - - - Gets a communication channel provider factory. - - - - - Gets the storage strategy factories. - - - - - Builds the plan. - - - - - - Represent metadata of message (command / event) metadata of - a communication channel (Pub/Sub, Event Source, REST, GraphQL). - It represent the operation's intent or represent event. - - - - - Get the metadata context - - - - - Initializes a new instance. - - The metadata. - The consuming cancellation - (stop consuming call-back on cancellation). - - - - Gets the metadata. - - - - - Cancel the consuming process. - - - - - Performs an implicit conversion. - - The instance. - - The result of the conversion. - - - - - Gets the max batch size of reading messages per shard. - The framework won't proceed to the next batch until all messages - in the batch complete (or timeout when it set to acknowledge on timeout). - - - - - Gets a value indicating whether to prevent the consumer - from being collect by the GC. - True by default, when you hold the subscription disposable - you can set it to false. as long as you keeping the disposable in - object that isn't candidate for being collected the consumer will stay alive. - - - true if [keep alive]; otherwise, false. - - - - - Gets the acknowledge behavior. - - - - - Gets the maximum messages to consume before detaching the subscription. - any number > 0 will activate this mechanism. - - - - - Gets the threshold duration which limit considering producer call as parent, - Beyond this period the consumer span will refer the producer as Link (rather than parent). - - - - - Base class for the consumer's code generator - - - - - Get parameter value from the announcement. - - The type of the parameter. - The argument. - Name of the argument. - The plan. - - - - - - Gets the parameter value. - - The type of the parameter. - The argument. - Name of the argument. - - - - - Consumer stage of an interception operation provider. - It can be use for variety of responsibilities like - flowing auth context or traces, producing metrics, etc. - - - - - - Interception operation. - - The metadata. - - The interceptor data which sets on the - producer stage of the interception. - - - - Consumer stage of an interception operation provider. - It can be use for variety of responsibilities like - flowing auth context or traces, producing metrics, etc. - - - - - - Interception operation. - - The metadata. - - The interceptor data which sets on the - producer stage of the interception. - - - - Subscription Bridge convention - - - - - Bridges to the subscriber implementation. - - The announcement. - The consumer bridge. - - - - - Channel provider responsible for passing the actual message - from producer to consumer. - - - - - Subscribe to the channel for specific metadata. - - The consumer plan. - The function. - The options. - The cancellation token. - - When completed - - - - - Gets the by identifier asynchronous. - - The entry identifier. - The plan. - The cancellation token. - - Size of the read buffer. - How many items to read on a single iteration. - - - - - - Responsible to load information from storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Load the bucket information. - - The meta fetch provider. - The current bucket (previous item in the chain). - The type of the storage. - The get property. - The cancellation. - - Either Segments or Interceptions. - - - - - Responsible of building instance from segmented data. - Segmented data is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Unclassify segmented data into an instance. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - The segments which was collect so far. - It start as Empty and flow though all the registered segmentation strategies. - The operation's key which represent the method call at the - producer proxy. - This way you can segment same type into different slot. - Name of the argument. - The options. - - Materialization of the segments. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Responsible of building instance from segmented data. - Segmented data is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Unclassify segmented data into an instance. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - The segments which was collect so far. - It start as Empty and flow though all the registered segmentation strategies. - The operation's key which represent the method call at the - producer proxy. - This way you can segment same type into different slot. - Name of the argument. - The options. - - Materialization of the segments. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Extract telemetry span's parent info - - - The meta. - The entries for extraction. - The injection strategy. - - - - diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/icon.png b/Consumers/Weknow.EventSource.Backbone.Consumers.Contracts/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 zcmcInX;c$e8%7Z#2qKEeP7GAal6`^1^ydax@nT7KmAx!LkYrTEJsrwo*9+j>H8D5qLyP zA>ZhYeqeMcNatbJ{RhodFc1O}BnQw6kx(pSC|H<}ybNTloW^6&A0hHk7RE_w5FNnr zM!UdL2u;P&upp5_McdPHL>iewr`w@P1fm0;;D9HRu|yh!LST?c=-CT{Sd;Sj3?H`J ztS#h?g$a?%B@8@1GBOeuNyfp_U_6mdr{f7EJc)!w5Lj81SPm$#VwuGp0~?ZoQh`J+ zfW>GfBfy2j2Kr%R73PP?CkXUXpXN(8V@0Em0g&#fTfp|y=i4d3!5k{OhB?*D$uq*`r z2S4VQ=e?0IT%-^nhoycnESyV}_Z$n_#YLGII{Hfv01CuPYHOve*_;Rrh-3zZVoM+~2!u~i4pIy}Kn{E!80HE1QJ(|la2TFqnH&&}-rEx;HSSkXLViAadUBGs&RpZ zu#^MykeHMII`8S?;w^>w0wJ;>^Ko}Zd%C*Nh;$kaOTrPAwU5X^-1QL1kVHq#o+e)? zY<46Rpg)!<0|1pLgM|T=B?0j;v(tjV3G?SU^ocey1VTvvrAj`*WH4VI2}mKQU?e4< zSBm(5@<0Ye{JZ|oE0h1X{@DP7A%Hj-LK-d}qx1-`Y)zliga6;meO&uX8=He8y-_)w zZ_3Ead|QXa2(=Vx+9W%@VB}kH*OTq!r}**hNzOq%SG~J?{F|DZSY>PN$}AMBmbFQ% zb+z}S=ut#7rfk^ugn`CzcSgc+e|?At<>GMV(}dwl4ZQk_UzV{ci>s=R-CkODXF%St zH6yA2DL&#xV8d@X^SX7%>)scPKYBF!^{wcfCqc&?epfS5UAk~8J~&PgWMbe|qaUvp zq(8A@LUqE_AunO5$E`olt^Pq(YNcVUiDKoGwU%|Ex3_g_-CrHy@l64NggWN%(?YAb ztMTn;yUWz=WV^J)MXO_+yW z+D#!;Prv$BuJ%>gvsaR(3T&Om`K2DY(>huodPD^pnfj&e`@|?=)($u8FBSP*U8ecV zqx*E1h}YaaHAd&rJB`(+dpN6%sm1jM7Yx5sTYq-_0vo#ooL03eU%srobt}69b#MQq zzc{Qq$au*UetJxZk)Jg?<1T7?L&syMiP0OekEh7VBfatHLwd&UPpaEzd}9N?{MKgE z>Ai%(8UsbxEoPS)%F?g{{~~#$g}bV!{c5>}_ct2U^7=wU>5kiUz`y!hN9^QbX0>s` z@gmWQ;ZA*ja-bI`*&r#bw6M}<)f(fI1s<{(kT7Z_FN@MMG}Tm-LpBXJyisF?ZH|Se zABaBB3x91MY+Lty!Hu1H39_=H0HZpftHH2G5LKCNghs6+`Tw~)E-Gx|>}$7-IeAL;`vd!qUu51PR~vQ&q}zn+2ti$P zjF-uPki5|1>-0l+N>te=O^u-e+zRId=iBd(woH}mx=Nsau(E0)UfO0A9<#accN(Z0 z?3-nrJnSWH#ctTW(apA5_ep4X$Wgaz4Oa_uL$(HQug}ii9ax^?y6wfKHJcI{JzX1x zB@a_?ZQh>MaE|yhov~+U-u5z8M_||5j;5FgzG=sA`iHxhF3;4j!PGeEMDEDYXcZp_ z@yRRynX+dlV=TwEU}j)u1Yh_J=gXAGH9qW}5=(`xEB#B}Eov2e9mt%b4qU()x4$VV zOKGyGimonVPc^PQvF;B>x|ZX)(7nNwfzu?@>Y6QG>2O1Hk59^A_U=B-T$=sigI@&L zYG*bXMXo=&RA4gjemu6f=rzV{+Z6$6i&O5%HMjIXye?mwagJGXYSH;C-IHfLdM~B6 zWShH%w2cu|E2;jYm}vo_o)xH!rws^f)ao#ksWivgY!fvkiF+z*^VHSsmR- z{Uyun;^Gq;{&w88eNRu@=x=td*h9gskmHYUe{x2grcKFaX>s_Ui3d^tJGXkA6+@IN;s|8DzA8(9W zFX)vwCPi}2u{|Sva&6P{&rD!>b=b*QwdW3Q$+wVjP+j#Mwk%t&*%9bMhVv$W_im&V zANu+a6ORSlt?D_YdjGq9uNMAf$voC<#G0=1&PuR3l%1D#xOa69p@BGQ7_d{nV(FV& zld+KWU*Ux@M{2u0r}_?h9B#>fm$-x2R+(S0_Q9jf{NerN4XfI&Y^klct4K5|I$f~# z;Um-QWiEbEmdaHH*X=$$zY_3jqLgX)xH`L&zVn5{t&)a`;o*teyn6}0 zT(v6^nr(-Udk?)c(vj>E$ws&AO1-|=WLqod^2?LwS~AyZblq#4R9hWgVzcSUyCK{c zH%E+hBqf1XAD+A2yOO%S++V*`aapc&NLqHY=B38j$EgFW2Jn1I)7Q<`sZ;m88`;xz zy330-=<33;*EHAhJ7=YWe#}r$?v4|CbC_L=hjeij<$?KD3=_jDg(5ag>T>aZdBV98 zih;OW|2nF-|IJszr*%!!LERztLz+W&#fM(L9$Dd7l-Q~=P!hX)#%ADlkCkbnJ - - - Weknow.EventSource.Backbone.Consumers - - - - - Base class for the consumer's code generator - - - - - Initializes a new instance. - - The plan. - Per method handler, handle methods calls. - - - - Initializes a new instance. - - The plan. - Per method handler, handle methods calls. - - - - Subscribes this instance. - - - - - - Represent single consuming subscription - - - - - Initializes a new subscription. - - The plan. - Per operation invocation handler, handle methods calls. - - - - Get parameter value from the announcement. - - The type of the parameter. - The argument. - Name of the argument. - - - - - - Represent the consuming completion.. - - - - - Handles consuming of single event. - - The argument. - - The acknowledge callback which will prevent message from - being re-fetch from same consumer group. - - - - - Release consumer. - - - A task that represents the asynchronous dispose operation. - - - - - Event Source consumer builder. - - - - - Event Source consumer builder. - - - - - Prevents a default instance of the class from being created. - - - - - Initializes a new instance. - - The plan. - - - - Choose the communication channel provider. - - The channel provider. - - - - - Adds the storage strategy (Segment / Interceptions). - Will use default storage (REDIS Hash) when empty. - When adding more than one it will to all, act as a fall-back (first win, can use for caching). - It important the consumer's storage will be in sync with this setting. - - Storage strategy provider. - Type of the target. - - - - - Attach configuration. - - - - - - - Include the environment as prefix of the stream key. - for example: production:partition-name:shard-name - - The environment. - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - The partition key. - - - - - Shard key represent physical sequence. - On the consumer side shard is optional - for listening on a physical source rather on the entire partition. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - The shard key. - - - - - - Responsible of building instance from segmented data. - Segmented data is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - The segmentation strategy. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Responsible of building instance from segmented data. - Segmented data is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - The segmentation strategy. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Registers the interceptor. - - The interceptor. - - - - - Registers the interceptor. - - The interceptor. - - - - - Attach logger. - - The logger. - - - - - Set resilience policy - - The policy. - - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - - The partition subscription (dispose to remove the subscription) - - _plan - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - The partition subscription (dispose to remove the subscription) - - _plan - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - The partition subscription (dispose to remove the subscription) - - _plan - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - - The partition subscription (dispose to remove the subscription) - - _plan - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - The partition subscription (dispose to remove the subscription) - - _plan - - - - Subscribe consumer. - - Per operation invocation handler, handle methods calls. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - - The partition subscription (dispose to remove the subscription) - - _plan - - - - Withes the cancellation token. - - The cancellation. - - - - - Build receiver (on demand data query). - - - - - - Initializes a new instance. - - The plan. - - - - Gets the asynchronous. - - The entry identifier. - The cancellation token. - - - - - Gets the asynchronous. - - The entry identifier. - The cancellation token. - - - - - Hold builder definitions. - Define the consumer execution pipeline. - - - - - Initializes a new instance. - - - - - Initializes a new instance. - - The copy from. - The channel. - The channel. - The environment. - The partition. - The shard. - The logger. - The options. - The segmentation strategies. - The interceptors. - The routes. - The cancellation token. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - Optional Name of the consumer. - Can use for observability. - The resilience policy. - The storage strategy. - - - - Gets the communication channel provider. - - - - - Gets the communication channel provider. - - - - - Gets the storage strategy. - - - - - Gets the storage strategies. - - - - - Gets the logger. - - - - - Environment (part of the stream key) - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - - - - Shard key represent physical sequence. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - - - - Gets the configuration. - - - - - Segmentation responsible of splitting an instance into segments. - Segments is how the Consumer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Consumer interceptors (Timing: after serialization). - - - The interceptors. - - - - - Gets the cancellation token. - - - - - Routes are sub-pipelines are results of merge operation - which can split same payload into multiple partitions or shards. - - - - - Gets the consumer group. - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - - - - - Optional Name of the consumer. - Can use for observability. - - - - - Gets or sets the invocation resilience policy. - - - - - Attach the channel. - - The channel. - - - - - Attach the Storage Strategy. - - The storage strategy. - - - - - Attach the LOGGER. - - The logger. - - - - - Attach the cancellation. - - The cancellation. - - - - - Attach the options. - - The options. - - - - - Attach the environment. - - The environment. - - - - - Attach the partition. - - The partition. - - - - - Attach the shard. - - The shard. - - - - - Attach the shard. - - The shard. - - - - - Set resilience policy - - The policy. - - - - - Adds the route. - - The route. - - - - - Adds the segmentation. - - The segmentation. - - - - - Adds the interceptor. - - The interceptor. - - - - - Set Consumer Group which allow a group of clients to cooperate - consuming a different portion of the same stream of messages - - - Consumer Group allow a group of clients to cooperate - consuming a different portion of the same stream of messages - - - Optional Name of the consumer. - Can use for observability. - - - - - - Builds this instance. - - - - - - Wrap Channel Storage with key filtering of the bucket. - Useful for 'Chain of Responsibility' by saving different parts - into different storage (For example GDPR's PII). - - - - - Initializes a new instance. - - The actual storage provider. - Type of the target. - - - - Determines whether is of the right target type. - - The type. - - - - - Load the bucket information. - - The meta fetch provider. - The current bucket (previous item in the chain). - The type of the storage. - The get property. - The cancellation. - - Either Segments or Interceptions. - - - - diff --git a/Consumers/Weknow.EventSource.Backbone.Consumers/icon.png b/Consumers/Weknow.EventSource.Backbone.Consumers/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 true - event-source, pub, sub, pubsub, messaging, reliable, redis, weknow, bnaya - https://medium.com/weknow-network - https://github.com/weknow-network/Event-Source-Backbone + event-source, pub, sub, pubsub, messaging, reliable, redis, bnaya + https://medium.com/@bnayae + https://github.com/bnayae/Event-Source-Backbone GitHub true MIT @@ -34,12 +34,7 @@ - - 1.1.274: [BREAKING CHANGES] Consumer options / consumer channel setting had changed - 1.2.17: - Support Inheritance - [BREAKING CHANGES] Renaming of Enum - + diff --git a/Weknow.EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs b/EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs similarity index 79% rename from Weknow.EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs rename to EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs index 37839af1..7dbd9ce3 100644 --- a/Weknow.EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs +++ b/EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source Generation Type diff --git a/Weknow.EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs b/EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs similarity index 91% rename from Weknow.EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs rename to EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs index 57c733ab..78e2d981 100644 --- a/Weknow.EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs +++ b/EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event source's version control. @@ -8,7 +8,7 @@ /// We don't expect a gap between ConsumeFrom to a lower version. /// Versions expect to start at 0, if no version specified It will consider version 0, /// - /// + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class EventSourceVersionAttribute : Attribute { diff --git a/Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs b/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs similarity index 81% rename from Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs rename to EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs index f5f5fb66..cce41ca7 100644 --- a/Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs +++ b/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs @@ -1,9 +1,9 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Mark for code generation /// - /// + /// [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] public class GenerateEventSourceAttribute : GenerateEventSourceBaseAttribute { diff --git a/Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs b/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs similarity index 96% rename from Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs rename to EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs index 8a06217c..b26e9df9 100644 --- a/Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs +++ b/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Mark for code generation diff --git a/Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs b/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs similarity index 91% rename from Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs rename to EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs index 2e72f608..aad1a55a 100644 --- a/Weknow.EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs +++ b/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { [Obsolete("Deprecated, use GenerateEventSourceAttribute instead", true)] [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] diff --git a/Weknow.EventSource.Backbone.Contracts/Bucket.cs b/EventSource.Backbone.Contracts/Bucket.cs similarity index 94% rename from Weknow.EventSource.Backbone.Contracts/Bucket.cs rename to EventSource.Backbone.Contracts/Bucket.cs index 4d7b4e32..53a3b0fe 100644 --- a/Weknow.EventSource.Backbone.Contracts/Bucket.cs +++ b/EventSource.Backbone.Contracts/Bucket.cs @@ -3,9 +3,9 @@ using System.Text; using System.Text.Json; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.EventSourceConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class Bucket : IEnumerable>> { @@ -186,7 +186,15 @@ public bool ContainsKey(string key) /// public bool TryGetValue(string key, out ReadOnlyMemory value) { - return _data.TryGetValue(key, out value); + if(_data.TryGetValue(key, out value)) + return true; + if(_data.TryGetValue(key.ToPascal(), out value)) + return true; + if(_data.TryGetValue(key.ToCamel(), out value)) + return true; + if(_data.TryGetValue(key.ToDash(), out value)) + return true; + return _data.TryGetValue(key.ToLower(), out value); } #endregion // TryGetValue diff --git a/Weknow.EventSource.Backbone.Contracts/BucketJsonConverter.cs b/EventSource.Backbone.Contracts/BucketJsonConverter.cs similarity index 95% rename from Weknow.EventSource.Backbone.Contracts/BucketJsonConverter.cs rename to EventSource.Backbone.Contracts/BucketJsonConverter.cs index 271997a0..732a4913 100644 --- a/Weknow.EventSource.Backbone.Contracts/BucketJsonConverter.cs +++ b/EventSource.Backbone.Contracts/BucketJsonConverter.cs @@ -1,11 +1,11 @@ using System.Text.Json; using System.Text.Json.Serialization; -using static Weknow.EventSource.Backbone.EventSourceOptions; +using static EventSource.Backbone.EventSourceOptions; using BucketData = System.Collections.Immutable.ImmutableDictionary>; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// diff --git a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs b/EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs similarity index 97% rename from Weknow.EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs rename to EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs index 8217cca9..60d31b0c 100644 --- a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Non-generics form of announcement representation, diff --git a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs b/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs similarity index 98% rename from Weknow.EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs rename to EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs index 7ba2d4e9..0cc80cfe 100644 --- a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Non-generics form of announcement representation, diff --git a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs b/EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs similarity index 87% rename from Weknow.EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs rename to EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs index 1603a33e..dce218ad 100644 --- a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs @@ -1,6 +1,6 @@ //using System.Text.Json.Serialization; -//using Weknow.EventSource.Backbone; +//using EventSource.Backbone; ////[JsonSerializable(typeof(Announcement))] ////[JsonSerializable(typeof(AnnouncementData))] diff --git a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs b/EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs similarity index 90% rename from Weknow.EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs rename to EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs index 34855a98..9b17bbdf 100644 --- a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// The message origin diff --git a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs b/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs similarity index 99% rename from Weknow.EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs rename to EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs index d27f6f10..be7fb769 100644 --- a/Weknow.EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// /// Event Id diff --git a/Weknow.EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs b/EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs similarity index 99% rename from Weknow.EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs rename to EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs index 1ad23b2b..deb2aed9 100644 --- a/Weknow.EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs +++ b/EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Immutable; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Id diff --git a/Weknow.EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs b/EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs similarity index 85% rename from Weknow.EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs rename to EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs index 4e8f1283..523cd4ae 100644 --- a/Weknow.EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs +++ b/EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Bucket storage type diff --git a/Weknow.EventSource.Backbone.Contracts/Env.cs b/EventSource.Backbone.Contracts/Env.cs similarity index 98% rename from Weknow.EventSource.Backbone.Contracts/Env.cs rename to EventSource.Backbone.Contracts/Env.cs index c0f01814..40d17630 100644 --- a/Weknow.EventSource.Backbone.Contracts/Env.cs +++ b/EventSource.Backbone.Contracts/Env.cs @@ -1,6 +1,6 @@ using static System.StringComparison; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Common Constants diff --git a/Weknow.EventSource.Backbone.Contracts/Weknow.EventSource.Backbone.Contracts.csproj b/EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj similarity index 70% rename from Weknow.EventSource.Backbone.Contracts/Weknow.EventSource.Backbone.Contracts.csproj rename to EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj index 861c2131..55b7b103 100644 --- a/Weknow.EventSource.Backbone.Contracts/Weknow.EventSource.Backbone.Contracts.csproj +++ b/EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj @@ -1,16 +1,16 @@  - Weknow.EventSource.Backbone + EventSource.Backbone + - - + diff --git a/Weknow.EventSource.Backbone.Contracts/EventSourceConstants.cs b/EventSource.Backbone.Contracts/EventSourceConstants.cs similarity index 88% rename from Weknow.EventSource.Backbone.Contracts/EventSourceConstants.cs rename to EventSource.Backbone.Contracts/EventSourceConstants.cs index 14c5d2b7..f687ae07 100644 --- a/Weknow.EventSource.Backbone.Contracts/EventSourceConstants.cs +++ b/EventSource.Backbone.Contracts/EventSourceConstants.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Constants @@ -28,8 +28,6 @@ public static class EventSourceConstants Converters = { EnumConvertor, - JsonDictionaryConverter.Default, - JsonImmutableDictionaryConverter.Default, JsonMemoryBytesConverterFactory.Default } }; diff --git a/Weknow.EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs b/EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs similarity index 98% rename from Weknow.EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs rename to EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs index a8eddd83..e8275bca 100644 --- a/Weknow.EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs +++ b/EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging; -namespace Weknow.EventSource.Backbone.Private +namespace EventSource.Backbone.Private { /// /// Best practice is to supply proper logger and diff --git a/Weknow.EventSource.Backbone.Contracts/EventSourceOptions.cs b/EventSource.Backbone.Contracts/EventSourceOptions.cs similarity index 88% rename from Weknow.EventSource.Backbone.Contracts/EventSourceOptions.cs rename to EventSource.Backbone.Contracts/EventSourceOptions.cs index 19f1610e..dd96f4c3 100644 --- a/Weknow.EventSource.Backbone.Contracts/EventSourceOptions.cs +++ b/EventSource.Backbone.Contracts/EventSourceOptions.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public record EventSourceOptions { @@ -30,8 +30,6 @@ public record EventSourceOptions Converters = { EnumConvertor, - JsonDictionaryConverter.Default, - JsonImmutableDictionaryConverter.Default, JsonMemoryBytesConverterFactory.Default } }; @@ -46,8 +44,6 @@ public record EventSourceOptions Converters = { EnumConvertor, - JsonDictionaryConverter.Default, - JsonImmutableDictionaryConverter.Default, JsonMemoryBytesConverterFactory.Default, JsonBucketConverter.Default } diff --git a/Weknow.EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs b/EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs similarity index 94% rename from Weknow.EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs rename to EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs index 83453caf..06fae550 100644 --- a/Weknow.EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs +++ b/EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Enable to replace the default serialization diff --git a/Weknow.EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs b/EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs similarity index 88% rename from Weknow.EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs rename to EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs index d5d53215..e4ec6147 100644 --- a/Weknow.EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs +++ b/EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public interface IInterceptorName { diff --git a/Weknow.EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs b/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs similarity index 97% rename from Weknow.EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs rename to EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs index e97b0104..b555d38f 100644 --- a/Weknow.EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs +++ b/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Plan routing identification diff --git a/Weknow.EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs b/EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs similarity index 90% rename from Weknow.EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs rename to EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs index 5f1a122a..120552a4 100644 --- a/Weknow.EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs +++ b/EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source cancellation builder. diff --git a/Weknow.EventSource.Backbone.Contracts/JsonDataSerializer.cs b/EventSource.Backbone.Contracts/JsonDataSerializer.cs similarity index 88% rename from Weknow.EventSource.Backbone.Contracts/JsonDataSerializer.cs rename to EventSource.Backbone.Contracts/JsonDataSerializer.cs index be9e7006..f8eb83bd 100644 --- a/Weknow.EventSource.Backbone.Contracts/JsonDataSerializer.cs +++ b/EventSource.Backbone.Contracts/JsonDataSerializer.cs @@ -1,14 +1,14 @@ using System.Text; using System.Text.Json; -using static Weknow.Text.Json.Constants; +using static System.Text.Json.Extension.Constants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Json serializer (this is the default serializer) /// - /// + /// internal class JsonDataSerializer : IDataSerializer { private readonly JsonSerializerOptions _options; diff --git a/Weknow.EventSource.Backbone.Contracts/TelemetryrExtensions.cs b/EventSource.Backbone.Contracts/TelemetryrExtensions.cs similarity index 97% rename from Weknow.EventSource.Backbone.Contracts/TelemetryrExtensions.cs rename to EventSource.Backbone.Contracts/TelemetryrExtensions.cs index f00ef7ee..bdcf94f6 100644 --- a/Weknow.EventSource.Backbone.Contracts/TelemetryrExtensions.cs +++ b/EventSource.Backbone.Contracts/TelemetryrExtensions.cs @@ -2,7 +2,7 @@ using OpenTelemetry.Trace; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class TelemetryrExtensions { diff --git a/EventSource.Backbone.Contracts/icon.png b/EventSource.Backbone.Contracts/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 - + diff --git a/Weknow.EventSource.Backbone/Weknow.EventSource.Backbone.xml b/EventSource.Backbone/EventSource.Backbone.xml similarity index 65% rename from Weknow.EventSource.Backbone/Weknow.EventSource.Backbone.xml rename to EventSource.Backbone/EventSource.Backbone.xml index 05b0920d..f8f66565 100644 --- a/Weknow.EventSource.Backbone/Weknow.EventSource.Backbone.xml +++ b/EventSource.Backbone/EventSource.Backbone.xml @@ -1,7 +1,7 @@ - Weknow.EventSource.Backbone + EventSource.Backbone diff --git a/EventSource.Backbone/icon.png b/EventSource.Backbone/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 + + diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs similarity index 89% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs index 4f5895d2..856c812d 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { ///

/// Origin environment of the message diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs similarity index 83% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs index 51f9eea4..28167fda 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Origin environment of the message diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs similarity index 99% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs index fc2b5fdd..3e1e063e 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs similarity index 92% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs index 3c4ac2c7..a3e07c31 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs similarity index 88% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs index dc798d88..6ca7bc34 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable configuration. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs similarity index 95% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs index eac5ef22..d8400b0a 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs similarity index 89% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs index 77af9241..b8181c92 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs similarity index 93% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs index a6719a85..59f9a2bf 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable configuration. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs similarity index 97% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs index 532b03a3..a3016e79 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Event Source producer builder. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs similarity index 94% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs index 8ad0b201..e6e8aba4 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible to save information to storage. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs similarity index 86% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs index c05cd7f4..6f197e6e 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs @@ -1,14 +1,14 @@  using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Enable configuration. /// - /// + /// public interface IProducerStoreStrategyBuilder : IProducerOptionsBuilder { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs similarity index 90% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs index 79a1c129..b17f4708 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source raw producer. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs similarity index 95% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs index d78e3734..9c106713 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs similarity index 90% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs index 7e79622c..c08eac56 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs similarity index 93% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs index e08b90f0..0edb06ba 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs similarity index 93% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs index b33ba84e..c87d0e56 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs similarity index 93% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs index 8caf5a80..829080b4 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs similarity index 88% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs index 7b1c054f..43dc12b2 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs @@ -1,5 +1,5 @@  -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Raw producer options diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs similarity index 81% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs index c8df5e37..7a5cd001 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// the type of the routing assignment diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs similarity index 92% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs index a8cf1abf..7f078983 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Event Source producer builder. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Weknow.EventSource.Backbone.Producers.Contracts.csproj b/Producers/EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj similarity index 71% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Weknow.EventSource.Backbone.Producers.Contracts.csproj rename to Producers/EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj index 5444bc49..c952ea69 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Weknow.EventSource.Backbone.Producers.Contracts.csproj +++ b/Producers/EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj @@ -1,7 +1,7 @@  - + diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs b/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs similarity index 88% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs index 27c8123a..a2643ca3 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs @@ -1,11 +1,11 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Producer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IProducerAsyncInterceptor : IInterceptorName { diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs b/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs similarity index 85% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs index 2d0bd1a0..3838e091 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs @@ -1,11 +1,11 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Producer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IProducerInterceptor : IInterceptorName { diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs b/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs similarity index 96% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs index bdf5cb21..b9aa5006 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Bridge segmentation diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs b/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs similarity index 89% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs index d2a80aff..c681d819 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs @@ -1,12 +1,12 @@ using System.Collections.Immutable; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Plan contract /// - /// + /// public interface IProducerPlan : IProducerPlanBase { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs b/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs similarity index 97% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs index 90f1d9f6..93041b44 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs similarity index 88% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs index ae2dad61..cc6e5997 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs @@ -2,14 +2,14 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Plan builder contract /// - /// + /// public interface IProducerPlanBuilder : IProducerPlanBase { /// diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs b/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs similarity index 98% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs index 8a9a98c3..f4d49c50 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs @@ -2,10 +2,10 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Private; +using EventSource.Backbone.Building; +using EventSource.Backbone.Private; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// @@ -443,7 +443,7 @@ public ProducerPlan AddForward(IProducerHooksBuilder forward) /// /// Not operational channel /// - /// + /// private class NopChannel : IProducerChannelProvider { public static readonly IProducerChannelProvider Empty = new NopChannel(); diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs b/Producers/EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs similarity index 94% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs rename to Producers/EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs index 181c49a3..852347c2 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs @@ -4,9 +4,9 @@ using Microsoft.Extensions.Logging; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.EventSourceConstants; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class ProducerExtensions { @@ -38,7 +38,7 @@ ValueTask Local(ILogger logger) /// /// Non strategy implementation /// - /// + /// private class VoidStorageStrategy : IProducerStorageStrategy { private readonly string _providerPrefix; diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs b/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs similarity index 99% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs rename to Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs index dd9c0562..7030e4d4 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Handle the producing pipeline diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs b/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs similarity index 94% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs index 84e73582..d24f484a 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Channel provider responsible for passing the actual message diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs b/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs similarity index 97% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs index 7414fb88..c1431b92 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible to save information to storage. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs b/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs similarity index 98% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs index c82637fc..1a36ff3e 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// Responsible of splitting an instance into segments. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs b/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs similarity index 97% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs index 759b2a6a..c5700f0b 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Responsible of splitting an instance into segments. diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs b/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs similarity index 98% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs rename to Producers/EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs index e39004cf..cf258ddb 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.Building +namespace EventSource.Backbone.Building { /// /// Bridge segmentation diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs b/Producers/EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs similarity index 97% rename from Producers/Weknow.EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs rename to Producers/EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs index b2d94f41..6674ab73 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs @@ -4,7 +4,7 @@ using OpenTelemetry; using OpenTelemetry.Context.Propagation; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class TelemetryrExtensions { diff --git a/Producers/EventSource.Backbone.Producers.Contracts/icon.png b/Producers/EventSource.Backbone.Producers.Contracts/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 /// Wrap Channel Storage with key filtering of the bucket. diff --git a/Producers/Weknow.EventSource.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs similarity index 99% rename from Producers/Weknow.EventSource.Backbone.Producers/Builder/ProducerBuilder.cs rename to Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs index e0e3be08..aef76f65 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs @@ -1,10 +1,10 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; using static System.String; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { ///

/// Event Source producer builder. @@ -553,7 +553,7 @@ IProducerOverrideBuildBuilder IProducerOverrideShardBuilder.Shard(string s /// /// Raw producer (useful for cluster migration) /// - /// + /// private class RawProducer : IRawProducer { private readonly IProducerPlan _plan; diff --git a/Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj b/Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj new file mode 100644 index 00000000..48c2cd0b --- /dev/null +++ b/Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Producers/Weknow.EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs b/Producers/EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs similarity index 93% rename from Producers/Weknow.EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs rename to Producers/EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs index e2a8eb6d..b58a1abf 100644 --- a/Producers/Weknow.EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs +++ b/Producers/EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class ProducerDefaultSegmentationStrategy : diff --git a/Producers/EventSource.Backbone.Producers/icon.png b/Producers/EventSource.Backbone.Producers/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - - Weknow.EventSource.Backbone.Producers.Contracts - - - -

- Origin environment of the message - - - - - Origin environment of the message - - The environment. - - - - - Event Source producer builder. - - - - - Gets the producer's plan. - - - - - Adds Producer interceptor (stage = after serialization). - - The interceptor. - - - - - Adds Producer interceptor (Timing: after serialization). - - The interceptor. - - - - - Register segmentation strategy, - Segmentation responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - A strategy of segmentation. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Register segmentation strategy, - Segmentation responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - A strategy of segmentation. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Event Source producer builder. - - - - - Attach logger. - - The logger. - - - - - Enable configuration. - - - - - Apply configuration. - - - - - Partition key represent logical group - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - The partition key. - - - - - Enable configuration. - - - - - Shard key represent physical sequence. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - The shard key. - - - - - Event Source producer builder. - - - - - or ValueTask). - Nothing but method allowed on this interface]]> - - The contract of the proxy / adapter - The factory. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - Bridge segmentation - - - - - Initializes a new instance. - - The synchronize. - - - - Unique name which represent the correlation - between the producer and consumer interceptor. - It's recommended to use URL format. - - - - - Interception operation. - - The metadata. - The segments. - - Data which will be available to the - consumer stage of the interception. - - - - - Responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Classifies instance into different segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - The segments which was collect so far. - It start as Empty and flow though all the registered segmentation strategies. - The operation's key which represent the method call at the - producer proxy. - This way you can segment same type into different slot. - Name of the argument. - The produced data. - The options. - - bytes for each segment or - Empty if don't responsible for segmentation of the type. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Bridge segmentation - - - - - Initializes a new instance. - - The synchronize. - - - - Try to classifies instance into different segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - EXPECTED to return the segments argument if it not responsible of - specific parameter handling. - - - The segments which was collect so far. - It start as Empty and flow though all the registered segmentation strategies. - The operation's key which represent the method call at the - producer proxy. - This way you can segment same type into different slot. - Name of the argument. - The produced data. - The options. - - bytes for each segment or - the segments argument if don't responsible for segmentation of the type. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Responsible to save information to storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Determines whether is of the right target type. - - The type. - - - - Enable configuration. - - - - - - Adds the storage strategy (Segment / Interceptions). - Will use default storage (REDIS Hash) when empty. - When adding more than one it will to all, act as a fall-back (first win, can use for caching). - It important the consumer's storage will be in sync with this setting. - - Storage strategy provider. - Type of the target. - The filter of which keys in the bucket will be store into this storage. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - or ValueTask). - Nothing but method allowed on this interface]]> - - The factory. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - Dynamic override of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The routing strategy. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - Override the environment. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The environment. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - Override the partition. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The partition. - The type. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - Override the shard. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The shard. - The type. - - - - - the type of the routing assignment - - - - - Event Source producer builder. - - - - - Choose the communication channel provider. - - The channel provider. - - - - - Merges multiple channels of same contract into single - producer for broadcasting messages via all channels. - - The first channel. - The second channel. - The others channels. - - - - - Producer stage of an interception operation provider. - It can be use for variety of responsibilities like - flowing auth context or traces, producing metrics, etc. - - - - - - Interception operation. - - The metadata. - The segments. - - Data which will be available to the - consumer stage of the interception. - - - - - Producer stage of an interception operation provider. - It can be use for variety of responsibilities like - flowing auth context or traces, producing metrics, etc. - - - - - - Interception operation. - - The metadata. - Data which will be available to the - consumer stage of the interception. - - - - Plan contract - - - - - - Gets the communication channel provider. - - - - - Gets the storage strategy. - By design the stream should hold minimal information while the main payload - is segmented and can stored outside of the stream. - This pattern will help us to split data for different reasons, for example GDPR PII (personally identifiable information). - - - - - Gets the forwards pipelines. - Result of merging multiple channels. - - - - - common plan properties - - - - - Producer interceptors (Timing: after serialization). - - - The interceptors. - - - - - Gets the logger. - - - - - Gets the configuration. - - - - - Segmentation responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Plan builder contract - - - - - - Gets the communication channel provider factory. - - - - - Gets the storage strategy. - By design the stream should hold minimal information while the main payload - is segmented and can stored outside of the stream. - This pattern will help us to split data for different reasons, for example GDPR PII (personally identifiable information). - - - - - Gets the forwards pipelines. - Result of merging multiple channels. - - - - - Buildings the plan. - - - - - - common plan routing properties - - - - - The origin environment of the message - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - - - - Shard key represent physical sequence. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - - - - Hold builder definitions. - Define the consumer execution pipeline. - - - - - Initializes a new instance. - - - - - Initializes a new instance. - - The copy from. - The channel. - The channel. - The environment. - The partition. - The shard. - The logger. - The options. - The segmentation strategies. - The interceptors. - The routes. - Result of merging multiple channels. - The forward channels. - The storage strategy. - - - - Gets the communication channel provider. - - - - - Gets the communication channel provider. - - - - - Gets the storage strategy. - By design the stream should hold minimal information while the main payload - is segmented and can stored outside of the stream. - This pattern will help us to split data for different reasons, for example GDPR PII (personally identifiable information). - - - - - Gets the storage strategy. - By design the stream should hold minimal information while the main payload - is segmented and can stored outside of the stream. - This pattern will help us to split data for different reasons, for example GDPR PII (personally identifiable information). - - - - - Gets the logger. - - - - - Origin environment of the message - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - - - - Shard key represent physical sequence. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - - - - Gets the configuration. - - - - - Segmentation responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Producer interceptors (Timing: after serialization). - - - The interceptors. - - - - - Gets the forwards pipelines. - Result of merging multiple channels. - - - - - Gets the forwards pipelines. - Result of merging multiple channels. - - - - - Routes are sub-pipelines are results of merge operation - which can split same payload into multiple partitions or shards. - - - - - Assign channel. - - The channel factory. - - - - - Attach logger. - - The logger. - - - - - Attach Storage Strategy. - - The storage strategy. - - - - - Withes the options. - - The options. - - - - - Withes the environment. - - The environment. - The partition. - The shard. - The type (only for partition and shard). - - - - - Withes the partition. - - The partition. - The shard. - The type. - - - - - Withes the shard. - - The shard. - The type. - - - - - Adds the route. - - The route. - - - - - Adds the segmentation. - - The segmentation. - - - - - Adds the interceptor. - - The interceptor. - - - - - Not operational channel - - - - - - Builds this instance. - - - - - - Handle the producing pipeline - CodeGenerator : generate class which inherit from ProducerPipeline - ---------- ProducerPipeline - pipeline which invoke on each call ----------- - classify-commands = - parameters.Select - CreateClassificationAdaptor(operation, argumentName, producedData) - return ClassifyArgumentAsync - SendAsync(operation, classifyAdaptors) // recursive - Channel.SendAsync(announcement) - - - - - Initializes a new instance. - - The plan. - - - - Initializes a new instance. - - The plan. - - - - Classify the operation payload from method arguments. - - - The operation. - Name of the argument. - The produced data. - - - MUST BE PROTECTED, called from the generated code - - - - - Classifies the operation's argument. - - - The plan. - The payload. - The operation. - Name of the argument. - The produced data. - - - - - Bridge classification of single operation's argument. - Get the argument data and pass it to the segmentation strategies. - - - The strategy. - The options. - The operation. - Name of the argument. - The produced data. - - - - - Call interceptors and store their intercepted data - (which will be use by the consumer's interceptors). - - The interceptors. - The metadata. - The payload. - The interceptors data. - - - - - Sends the produced data via the channel. - - The operation. - The classify strategy adaptors. - - - MUST BE PROTECTED, called from the generated code - - - - - Sends the produced data via the channel. - - The plan. - The identifier. - The payload. - The interceptors data. - The operation. - The classify strategy adaptors. - - - - - Channel provider responsible for passing the actual message - from producer to consumer. - - - - - Sends raw announcement. - - The raw announcement data. - The storage strategy. - - Return the message id - - - - - Responsible to save information to storage. - The information can be either Segmentation or Interception. - When adding it via the builder it can be arrange in a chain in order of having - 'Chain of Responsibility' for saving different parts into different storage (For example GDPR's PII). - Alternative, chain can serve as a cache layer. - - - - - Saves the bucket information. - - The identifier. - Either Segments or Interceptions. - The type. - The metadata. - The cancellation. - - Array of metadata entries which can be used by the consumer side storage strategy, in order to fetch the data. - - - - - Responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Try to classifies instance into different segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - EXPECTED to return the segments argument if it not responsible of - specific parameter handling. - - - The segments which was collect so far. - It start as Empty and flow though all the registered segmentation strategies. - The operation's key which represent the method call at the - producer proxy. - This way you can segment same type into different slot. - Name of the argument. - The produced data. - The options. - - bytes for each segment or - the segments argument if don't responsible for segmentation of the type. - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Inject telemetry span to the channel property. - - - The activity. - The meta. - The entries builder. - The injection strategy. - - - - diff --git a/Producers/Weknow.EventSource.Backbone.Producers.Contracts/icon.png b/Producers/Weknow.EventSource.Backbone.Producers.Contracts/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 - - - - - - - - - - - diff --git a/Producers/Weknow.EventSource.Backbone.Producers/Weknow.EventSource.Backbone.Producers.xml b/Producers/Weknow.EventSource.Backbone.Producers/Weknow.EventSource.Backbone.Producers.xml deleted file mode 100644 index c2f1e6a5..00000000 --- a/Producers/Weknow.EventSource.Backbone.Producers/Weknow.EventSource.Backbone.Producers.xml +++ /dev/null @@ -1,286 +0,0 @@ - - - - Weknow.EventSource.Backbone.Producers - - - - - Wrap Channel Storage with key filtering of the bucket. - Useful for 'Chain of Responsibility' by saving different parts - into different storage (For example GDPR's PII). - - - - - Initializes a new instance. - - The actual storage provider. - The filter according to keys. - Type of the target. - - - - Determines whether is of the right target type. - - The type. - - - - - Saves the bucket information. - - The identifier. - Either Segments or Interceptions. - The type. - The metadata. - The cancellation. - - Array of metadata entries which can be used by the consumer side storage strategy, in order to fetch the data. - - - - - Event Source producer builder. - - - - - Event Source producer builder. - - - - - Initializes a new instance. - - - - - Initializes a new instance. - - The plan. - - - - Gets the producer's plan. - - - - - Merges multiple channels of same contract into single - producer for broadcasting messages via all channels. - - The first channel. - The second channel. - The others channels. - - - - - - Choose the communication channel provider. - - The channel provider. - - - - - Adds the storage strategy (Segment / Interceptions). - Will use default storage (REDIS Hash) when empty. - When adding more than one it will to all, act as a fall-back (first win, can use for caching). - It important the consumer's storage will be in sync with this setting. - - Storage strategy provider. - Type of the target. - The filter of which keys in the bucket will be store into this storage. - - - - - Origin environment of the message - - - - - - Partition key represent logical group of - event source shards. - For example assuming each ORDERING flow can have its - own messaging sequence, yet can live concurrency with - other ORDER's sequences. - The partition will let consumer the option to be notify and - consume multiple shards from single consumer. - This way the consumer can handle all orders in - central place without affecting sequence of specific order - flow or limiting the throughput. - - The partition key. - - - - - Shard key represent physical sequence. - Use same shard when order is matter. - For example: assuming each ORDERING flow can have its - own messaging sequence, in this case you can split each - ORDER into different shard and gain performance bust.. - - The shard key. - - - - - - Apply configuration. - - - - - - - - Register segmentation strategy, - Segmentation responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - A strategy of segmentation. - - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Register segmentation strategy, - Segmentation responsible of splitting an instance into segments. - Segments is how the producer sending its raw data to - the consumer. It's in a form of dictionary when - keys represent the different segments - and the value represent serialized form of the segment's data. - - A strategy of segmentation. - - - - Examples for segments can be driven from regulation like - GDPR (personal, non-personal data), - Technical vs Business aspects, etc. - - - - - Adds Producer interceptor (stage = after serialization). - - The interceptor. - - - - - - Adds Producer interceptor (Timing: after serialization). - - The interceptor. - - - - - - Attach logger. - - The logger. - - - - - or ValueTask). - Nothing but method allowed on this interface]]> - - The contract of the proxy / adapter - The factory. - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - - - Enable dynamic transformation of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - - - - - Initializes a new instance. - - The plan. - - - - or ValueTask). - Nothing but method allowed on this interface]]> - - The factory. - - - - - Dynamic override of the stream id before sending. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The routing strategy. - - - - - Override the environment. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The environment. - - - - - Override the partition. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The partition. - The type. - - - - - Override the shard. - Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - - The shard. - The type. - - - - diff --git a/Producers/Weknow.EventSource.Backbone.Producers/icon.png b/Producers/Weknow.EventSource.Backbone.Producers/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 /// Entity mapper is responsible of mapping announcement to DTO generated from SequenceOperationsConsumer ///
/// - [GeneratedCode("Weknow.EventSource.Backbone.SrcGen", "1.1.141.0")] + [GeneratedCode("EventSource.Backbone.SrcGen", "1.1.141.0")] public static class SequenceOperationsConsumerEntityMapperExtensions1 { /// diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs similarity index 92% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs index 8fbcf656..49b64710 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs similarity index 86% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs index 9f3aa59e..66bafdc3 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { [GenerateEventSource(EventSourceGenType.Consumer)] public interface IEventFlowStage1 diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs similarity index 89% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs index cf6ba9af..af129900 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { /// /// use to test consumer which have partial operation from IEventFlow diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs similarity index 94% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs index 43d43f99..554cd273 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { /// /// The sequence operations. diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs similarity index 72% rename from src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs index 57c06983..a0fcc466 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities; +namespace EventSource.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs similarity index 75% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs index c7aa7be1..40bb29e6 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities; +namespace EventSource.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs b/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs similarity index 74% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs rename to Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs index 33f3d379..81823de0 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities; +namespace EventSource.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs b/Tests/EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs similarity index 94% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs index 56e9ddbc..3d0c6a1d 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs @@ -6,9 +6,9 @@ using Xunit; using Xunit.Abstractions; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { public class DeleteKeysTests { diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 7eb5bce8..9471d0c0 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -7,8 +7,8 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; @@ -19,7 +19,7 @@ // TODO: [bnaya 2020-10] ensure message order(cancel ack should cancel all following messages) // TODO: [bnaya 2020-10] check for no pending -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { /// /// The end to end explicit tests. diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs index add93a95..9241576f 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -7,17 +7,17 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { public class EndToEndStressTests : IDisposable { diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs similarity index 99% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs index 1edcdc70..b700ce5f 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs @@ -10,19 +10,19 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Enums; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.Enums; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static Weknow.EventSource.Backbone.EventSourceConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.EventSourceConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs b/Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs new file mode 100644 index 00000000..293aa3d4 --- /dev/null +++ b/Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs @@ -0,0 +1,5 @@ +namespace EventSource.Backbone.UnitTests.Entities +{ + public record Person(int Id, string Name); + +} diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Entities/User.cs b/Tests/EventSource.Backbone.IntegrationTests/Entities/User.cs similarity index 93% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Entities/User.cs rename to Tests/EventSource.Backbone.IntegrationTests/Entities/User.cs index 3f7c31ff..7381d49c 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Entities/User.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/Entities/User.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { public record User { diff --git a/Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj b/Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj new file mode 100644 index 00000000..9f4a9bfe --- /dev/null +++ b/Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj @@ -0,0 +1,64 @@ + + + + Debug;Release;Gen + false + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs b/Tests/EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs similarity index 92% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs index 22a8c659..82aeef47 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { public class InheritanceS3StoreStrategyTests : InheritanceTests { diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs similarity index 89% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/InheritanceTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs index a9935719..3be2986a 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs @@ -10,17 +10,17 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Enums; +using EventSource.Backbone.Building; +using EventSource.Backbone.Enums; using Xunit; using Xunit.Abstractions; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { /// /// The end to end tests. @@ -202,21 +202,47 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() await producer.BAsync(now); await producer.DerivedAsync("Hi"); - CancellationToken cancellation = GetCancellationToken(20); - + CancellationToken cancellation = GetCancellationToken(10); + int i = 0; + var tcs = new TaskCompletionSource(); #region Prepare var hash = new ConcurrentDictionary(); A.CallTo(() => _subscriberA.AAsync(1)) - .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if(Interlocked.Increment(ref i) == 3) + tcs.SetResult(i); + }); A.CallTo(() => _subscriberB.BAsync(A.Ignored)) - .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if(Interlocked.Increment(ref i) == 3) + tcs.SetResult(i); + }); A.CallTo(() => _subscriberAB.DerivedAsync("Hi")) - .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if(Interlocked.Increment(ref i) == 3) + tcs.SetResult(i); + }); A.CallTo(() => _subscriberAB.AAsync(1)) - .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if(Interlocked.Increment(ref i) == 3) + tcs.SetResult(i); + }); A.CallTo(() => _subscriberAB.BAsync(A.Ignored)) - .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if(Interlocked.Increment(ref i) == 3) + tcs.SetResult(i); + }); #endregion // Prepare @@ -252,9 +278,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - await subscriptionA.Completion; - await subscriptionB.Completion; - await subscriptionAB.Completion; + await tcs.Task.WithCancellation(new CancellationTokenSource(TimeSpan.FromSeconds(20)).Token); // new Task[] { subscriptionA.Completion, subscriptionB.Completion, subscriptionAB.Completion }.WhenN(2); #region Validation @@ -263,7 +287,6 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() Assert.True(hash.All(m => m.Value == 1)); #endregion // Validation - } #endregion // Inheritance_ConsumerCooperation_Succeed_Test diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs rename to Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs index bbc1432b..0cb39cdb 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -4,14 +4,14 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; using Xunit; using Xunit.Abstractions; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/MigrationTest.cs rename to Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs index ad647a07..6e6eadba 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs @@ -7,18 +7,18 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.Enums; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.Enums; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -using static Weknow.EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs b/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs similarity index 88% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs index 26e52cb4..a6451cf9 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { public class S3StoreStrategyExplicitTests : EndToEndExplicitTests { diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs b/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs similarity index 92% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs rename to Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs index db27a135..5aa8f890 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs +++ b/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { public class S3StoreStrategyStressTests : EndToEndStressTests { diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs b/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs similarity index 92% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs rename to Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs index d173ba11..df70df1a 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone.Tests +namespace EventSource.Backbone.Tests { public class S3StoreStrategyTests : EndToEndTests { diff --git a/Tests/EventSource.Backbone.IntegrationTests/icon.png b/Tests/EventSource.Backbone.IntegrationTests/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 /// In-Memory Channel (excellent for testing) ///

- /// + /// public class ProducerTestChannel : IProducerChannelProvider { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs b/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs similarity index 95% rename from Tests/Weknow.EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs rename to Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs index 4ca99a9b..8ff3c407 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs @@ -2,12 +2,12 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class ConsumerBuilderTests { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs b/Tests/EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs similarity index 93% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs rename to Tests/EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs index 25f8c915..71d8936d 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs +++ b/Tests/EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { [GenerateEventSource(EventSourceGenType.Producer, Name = "ISequenceOfProducer")] [GenerateEventSource(EventSourceGenType.Consumer, Name = "ISequenceOfConsumer")] diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs b/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs similarity index 94% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs rename to Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs index 2b3b0244..ab81dac7 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs +++ b/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { /// diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs b/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs similarity index 88% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs rename to Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs index ea5a9851..3536128e 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs +++ b/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { /// /// Test contract diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/EndToEndTests.cs b/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.UnitTests/EndToEndTests.cs rename to Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs index 898ca3e8..088794cd 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/EndToEndTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class EndToEndTests { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Entities/User.cs b/Tests/EventSource.Backbone.UnitTests/Entities/User.cs similarity index 95% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Entities/User.cs rename to Tests/EventSource.Backbone.UnitTests/Entities/User.cs index 3c991a06..c4a14a75 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Entities/User.cs +++ b/Tests/EventSource.Backbone.UnitTests/Entities/User.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { /// /// The user. diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/EventIdTests.cs b/Tests/EventSource.Backbone.UnitTests/EventIdTests.cs similarity index 97% rename from Tests/Weknow.EventSource.Backbone.UnitTests/EventIdTests.cs rename to Tests/EventSource.Backbone.UnitTests/EventIdTests.cs index e5ecfe62..7b817333 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/EventIdTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/EventIdTests.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class EventKeyTests { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Weknow.EventSource.Backbone.UnitTests.csproj b/Tests/EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj similarity index 50% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Weknow.EventSource.Backbone.UnitTests.csproj rename to Tests/EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj index a54c621d..e46b094c 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Weknow.EventSource.Backbone.UnitTests.csproj +++ b/Tests/EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj @@ -26,17 +26,17 @@ - - - - - - - + + + + + + + - + diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs b/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs similarity index 97% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs rename to Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs index 39a0296c..08096990 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs +++ b/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { public class SequenceOfProducerFactory : ProducerPipeline, ISequenceOfProducer diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs b/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs similarity index 84% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs rename to Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs index 23694a01..ea6e42c3 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs +++ b/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { public static class SequenceOfProducerFactoryExtensions { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs b/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs similarity index 95% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs rename to Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs index 485a3e42..db3d8319 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs +++ b/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks.Dataflow; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { public class SequenceOperationsConsumer : ISequenceOperationsConsumer { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs b/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs rename to Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs index 1dda2835..8f4c545a 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs +++ b/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { public class SequenceOperationsProducerFactory : ProducerPipeline, ISequenceOperationsProducer diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs b/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs similarity index 77% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs rename to Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs index f8ea7315..a65cc15b 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs +++ b/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; -namespace Weknow.EventSource.Backbone.UnitTests.Entities +namespace EventSource.Backbone.UnitTests.Entities { public static class SequenceOperationsProducerFactoryExtensions { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/InheritGenerationTest.cs b/Tests/EventSource.Backbone.UnitTests/InheritGenerationTest.cs similarity index 87% rename from Tests/Weknow.EventSource.Backbone.UnitTests/InheritGenerationTest.cs rename to Tests/EventSource.Backbone.UnitTests/InheritGenerationTest.cs index 4635d9d9..be9a26d7 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/InheritGenerationTest.cs +++ b/Tests/EventSource.Backbone.UnitTests/InheritGenerationTest.cs @@ -1,11 +1,11 @@ -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class InheritGenerationTest { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/ProducerBuilderTests.cs b/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.UnitTests/ProducerBuilderTests.cs rename to Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs index 3c80cee2..b4884e41 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/ProducerBuilderTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class ProducerBuilderTests { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/S3OptionsTests.cs b/Tests/EventSource.Backbone.UnitTests/S3OptionsTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.UnitTests/S3OptionsTests.cs rename to Tests/EventSource.Backbone.UnitTests/S3OptionsTests.cs index 6e3353b8..11a3faa2 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/S3OptionsTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/S3OptionsTests.cs @@ -2,7 +2,7 @@ -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class S3OptionsTests diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/SerializationTests.cs b/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs similarity index 95% rename from Tests/Weknow.EventSource.Backbone.UnitTests/SerializationTests.cs rename to Tests/EventSource.Backbone.UnitTests/SerializationTests.cs index bf0953ee..40f1d610 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/SerializationTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class SerializationTests { @@ -73,7 +73,8 @@ public void Announcement_Serialization_Test() var deserialize = serializer.Deserialize(buffer); Assert.Equal(announcement.Metadata, deserialize.Metadata); - Assert.True(deserialize.Segments.TryGetValue("X", out var arr)); + Assert.True(announcement.Segments.TryGetValue("X", out var arr)); + Assert.True(deserialize.Segments.TryGetValue("X", out arr)); Assert.Equal(1, arr.Span[0]); Assert.Equal(2, arr.Span[1]); } diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/SourceMigrationTests.cs b/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.UnitTests/SourceMigrationTests.cs rename to Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs index a4045afb..60748f42 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/SourceMigrationTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs @@ -5,15 +5,15 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class SourceMigrationTests { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/StoreStrategyTests.cs b/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.UnitTests/StoreStrategyTests.cs rename to Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs index 0daaad8b..ba171803 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/StoreStrategyTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class StoreStrategyTests { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs similarity index 77% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs rename to Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs index d26fd76a..7c9c1a45 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs +++ b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs @@ -1,12 +1,12 @@ -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.UnitTests.Entities; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// In-Memory Channel (excellent for testing) /// - /// + /// public class SimpleEventSubscription : SimpleEventSubscriptionBase { private readonly ISimpleEventConsumer _target; diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs similarity index 95% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs rename to Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs index 66dd490c..b034f41f 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs +++ b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.UnitTests.Entities; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs similarity index 90% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs rename to Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs index b6e08c00..3b9d839a 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs +++ b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs @@ -1,12 +1,12 @@ -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.UnitTests.Entities; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { /// /// In-Memory Channel (excellent for testing) /// - /// + /// public class SimpleEventSubscriptionBridge : ISubscriptionBridge { private readonly ISimpleEventConsumer _target; diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs similarity index 72% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs rename to Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs index f7d47b32..8ee0d667 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs +++ b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs @@ -1,7 +1,7 @@ -using Weknow.EventSource.Backbone.Building; -using Weknow.EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.Building; +using EventSource.Backbone.UnitTests.Entities; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public static class SimpleEventSubscriptionBridgeExtensions { diff --git a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs similarity index 81% rename from Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs rename to Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs index 0d7aed98..6709fcc8 100644 --- a/Tests/Weknow.EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs +++ b/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs @@ -1,7 +1,7 @@ -using Weknow.EventSource.Backbone.UnitTests.Entities; -using Weknow.EventSource.Backbone.UnitTests.Entities.Hidden; +using EventSource.Backbone.UnitTests.Entities; +using EventSource.Backbone.UnitTests.Entities.Hidden; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { public class SimpleEventSubscriptionFromGen : SimpleEventConsumerBase diff --git a/Tests/EventSource.Backbone.UnitTests/icon.png b/Tests/EventSource.Backbone.UnitTests/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs b/Tests/EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs similarity index 95% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs rename to Tests/EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs index f9b84fb5..a7284f4f 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace Weknow.EventSource.Backbone.WebEventTest +namespace EventSource.Backbone.WebEventTest { [GenerateEventSource(EventSourceGenType.Consumer)] [GenerateEventSource(EventSourceGenType.Producer)] diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs b/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs similarity index 98% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs rename to Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs index b0e70a07..04fcb3fd 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs @@ -5,7 +5,7 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone.WebEventTest.Controllers +namespace EventSource.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] [ApiController] diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs b/Tests/EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs similarity index 93% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs rename to Tests/EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs index dc8ad944..2a84aca3 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; -namespace Weknow.EventSource.Backbone.WebEventTest.Controllers +namespace EventSource.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] [ApiController] diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/TestController.cs b/Tests/EventSource.Backbone.WebEventTest/Controllers/TestController.cs similarity index 97% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/TestController.cs rename to Tests/EventSource.Backbone.WebEventTest/Controllers/TestController.cs index 00f3ddb2..5812b788 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Controllers/TestController.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Controllers/TestController.cs @@ -2,7 +2,7 @@ using StackExchange.Redis; -namespace Weknow.EventSource.Backbone.WebEventTest.Controllers +namespace EventSource.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] [ApiController] diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Dockerfile b/Tests/EventSource.Backbone.WebEventTest/Dockerfile similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Dockerfile rename to Tests/EventSource.Backbone.WebEventTest/Dockerfile diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Dockerfile.develop b/Tests/EventSource.Backbone.WebEventTest/Dockerfile.develop similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Dockerfile.develop rename to Tests/EventSource.Backbone.WebEventTest/Dockerfile.develop diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Entities/Address.cs b/Tests/EventSource.Backbone.WebEventTest/Entities/Address.cs similarity index 58% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Entities/Address.cs rename to Tests/EventSource.Backbone.WebEventTest/Entities/Address.cs index 5ac69bfd..1a1b60c8 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Entities/Address.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Entities/Address.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.WebEventTest +namespace EventSource.Backbone.WebEventTest { public record Address(string country, string city, string street); } diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Entities/Person.cs b/Tests/EventSource.Backbone.WebEventTest/Entities/Person.cs similarity index 58% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Entities/Person.cs rename to Tests/EventSource.Backbone.WebEventTest/Entities/Person.cs index 5c65ab0e..7ff152ff 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Entities/Person.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Entities/Person.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.WebEventTest +namespace EventSource.Backbone.WebEventTest { public record Person(int Id, string Name, Address? Address = null); diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Weknow.EventSource.Backbone.WebEventTest.csproj b/Tests/EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj similarity index 55% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Weknow.EventSource.Backbone.WebEventTest.csproj rename to Tests/EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj index 976175c8..bdd72dd4 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Weknow.EventSource.Backbone.WebEventTest.csproj +++ b/Tests/EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj @@ -24,16 +24,16 @@ - - - - - - + + + + + + - + diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/HostedServiceBase.cs b/Tests/EventSource.Backbone.WebEventTest/Jobs/HostedServiceBase.cs similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/HostedServiceBase.cs rename to Tests/EventSource.Backbone.WebEventTest/Jobs/HostedServiceBase.cs diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs b/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs similarity index 97% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs rename to Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs index 7ab7ee6e..4ee43b9a 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs @@ -1,10 +1,10 @@ using System.Text.Json; -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; // TODO: Register the service at the Program.cs file services.AddHostedService<...> -namespace Weknow.EventSource.Backbone.WebEventTest.Jobs +namespace EventSource.Backbone.WebEventTest.Jobs { ///

/// MicroDemo Event Source Listener diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs b/Tests/EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs similarity index 96% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs rename to Tests/EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs index a48ad18b..9bd93bf8 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs @@ -1,10 +1,10 @@ -using Weknow.EventSource.Backbone.Building; +using EventSource.Backbone.Building; // TODO: Register the service at the Program.cs file services.AddHostedService<...> -namespace Weknow.EventSource.Backbone.WebEventTest.Jobs +namespace EventSource.Backbone.WebEventTest.Jobs { /// /// MicroDemo Event Source Listener diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Program.cs b/Tests/EventSource.Backbone.WebEventTest/Program.cs similarity index 96% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Program.cs rename to Tests/EventSource.Backbone.WebEventTest/Program.cs index 45f06de9..278cf84c 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Program.cs @@ -6,8 +6,8 @@ using StackExchange.Redis; -using Weknow.EventSource.Backbone.WebEventTest; -using Weknow.EventSource.Backbone.WebEventTest.Jobs; +using EventSource.Backbone.WebEventTest; +using EventSource.Backbone.WebEventTest.Jobs; const string ENV = $"test"; @@ -16,7 +16,7 @@ IWebHostEnvironment environment = builder.Environment; string env = environment.EnvironmentName; string appName = environment.ApplicationName; -string shortAppName = appName.Replace("Weknow.", string.Empty) +string shortAppName = appName.Replace("", string.Empty) .Replace("Backend.", string.Empty); diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/Properties/launchSettings.json b/Tests/EventSource.Backbone.WebEventTest/Properties/launchSettings.json similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/Properties/launchSettings.json rename to Tests/EventSource.Backbone.WebEventTest/Properties/launchSettings.json diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs similarity index 97% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs rename to Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 0406c81f..4785d415 100644 --- a/Tests/Weknow.EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -1,6 +1,6 @@ -using Weknow.EventSource.Backbone; +using EventSource.Backbone; -using Weknow.EventSource.Backbone.WebEventTest; +using EventSource.Backbone.WebEventTest; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/TestSampler.cs b/Tests/EventSource.Backbone.WebEventTest/TestSampler.cs similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/TestSampler.cs rename to Tests/EventSource.Backbone.WebEventTest/TestSampler.cs diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/appsettings.Development.json b/Tests/EventSource.Backbone.WebEventTest/appsettings.Development.json similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/appsettings.Development.json rename to Tests/EventSource.Backbone.WebEventTest/appsettings.Development.json diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/appsettings.json b/Tests/EventSource.Backbone.WebEventTest/appsettings.json similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/appsettings.json rename to Tests/EventSource.Backbone.WebEventTest/appsettings.json diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/azds.yaml b/Tests/EventSource.Backbone.WebEventTest/azds.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/azds.yaml rename to Tests/EventSource.Backbone.WebEventTest/azds.yaml diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml diff --git a/Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml b/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml similarity index 100% rename from Tests/Weknow.EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml rename to Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/icon.png b/Tests/EventSource.Backbone.WebEventTest/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - - Debug;Release;Gen - false - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/icon.png b/Tests/Weknow.EventSource.Backbone.IntegrationTests/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 - - - Weknow.EventSource.Backbone.Contracts - - - -

- When true, it will only generate the contract interface(i.e. won't generate the bridge). - - - - - The name of the interface. - If missing the generator will use a convention. - - - - - The Namespace. - If missing the generator will use a convention. - - - - - Type of the generation - - - - - Empty - - - - - Prevents a default instance of the class from being created. - - - - - Initializes a new instance. - - The data. - - - - Adds an element with the specified key and value to the bucket. - - The key. - The value. - - - - - Adds an elements with the specified key and value to the bucket. - - - - - - - Adds the specified key/value pairs to the bucket. - - The bucket. - - - - - Adds an elements with the specified key and value to the bucket if the key doesn't exists. - - - - - - - Adds an elements with the specified key and value to the bucket if the key doesn't exists. - - - - - - - Adds the specified key/value pairs to the bucket if the key doesn't exists. - - The bucket. - - - - - Removes keys from the bucket. - - The keys. - - - - - Removes keys from the bucket. - - The keys. - - - - - Removes items from the bucket. - - The filter by key. - - - - - Determines whether the specified key contains key. - - The key. - - true if the specified key contains key; otherwise, false. - - - - - Gets the value associated with the specified key. - - The key. - The value. - - - - - Gets the value associated with the specified key. - - The key. - The value. - - - - - Gets the keys. - - - - - Performs an implicit conversion. - - The data. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The instance. - - The result of the conversion. - - - - - Returns an enumerator that iterates through the collection. - - - An enumerator that can be used to iterate through the collection. - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Non-generics form of announcement representation, - used to transfer data via channels. - - - - - Gets or sets the metadata. - - - - - Gets or sets the segments. - Segmentation is done at the sending side, - by Segmentation provider which can be register in order - to segments different parts of the messages. - The motivation of segmentation can come from regulation like - GDPR (right to erasure: https://gdpr-info.eu/). - - - Segmentation provider can split the message - into personal and non-personal segments. - - - - - Interception data (each interceptor responsible of it own data). - Interception can be use for variety of responsibilities like - flowing auth context or traces, producing metrics, etc. - - - - - Non-generics form of announcement representation, - used to transfer data via channels. - - - - - Gets or sets the segments. - Segmentation is done at the sending side, - by Segmentation provider which can be register in order - to segments different parts of the messages. - The motivation of segmentation can come from regulation like - GDPR (right to erasure: https://gdpr-info.eu/). - - - Segmentation provider can split the message - into personal and non-personal segments. - - - - - Performs an implicit conversion. - - The announcement. - - The result of the conversion. - - - - - - - Unlike the segments, this part can be flow with - message & will be set as async-context.]]> - - - - - The message identifier. - - - - - The sending time. - - - - - Gets or sets the operation. - - - - - Gets the origin environment of the message. - - - - - Gets or sets the partition. - - - - - Gets or sets the shard. - - - - - Gets or sets the shard. - - - - - Metadata extensions - - - - - Calculation of Duration since produce time - - - - - Gets the partition:shard as key. - - - - - Indicate whether it an empty metadata - - - - - Event Id - - - - - Initializes a new instance. - - The identifier. - - - - Converts to string. - - - A that represents this instance. - - - - - Performs an implicit conversion. - - The identifier. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The identifier. - - The result of the conversion. - - - - - Performs an implicit conversion from to . - - The identifier. - - The result of the conversion. - - - - - Event Id - - - - - Initializes a new instance. - - The keys. - - - - Initializes a new instance. - - The keys. - - - - Initializes a new instance. - - The keys. - - - - Initializes a new instance. - - The keys. - - - - Gets the with the specified i. - - The i. - - - - - Returns an enumerator that iterates through a collection. - - - An object that can be used to iterate through the collection. - - - - - Returns an enumerator that iterates through the collection. - - - An enumerator that can be used to iterate through the collection. - - - - - - Converts to string. - - - A that represents this instance. - - - - - Performs an implicit conversion. - - The identifiers. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The identifiers. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The identifiers. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The identifiers. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The identifiers. - - The result of the conversion. - - - - - Performs an implicit conversion. - - The identifiers. - - The result of the conversion. - - - - - Performs an implicit conversion. - Expecting single result - - The identifiers. - - The result of the conversion. - - - - - - - Performs an implicit conversion from to . - - The identifiers. - - The result of the conversion. - - - - - Bucket storage type - - - - - Constants - - - - - The name of redis consumer channel source - - - - - The name of redis producer channel source - - - - - Best practice is to supply proper logger and - not using this class.Default logger. - This class use Trace logger just in case the other logger is missing. - - - - - - - The default - - - - - Begins a logical operation scope. - - The type of the state to begin scope for. - The identifier for the scope. - - An that ends the logical operation scope on dispose. - - - - - Checks if the given is enabled. - - level to be checked. - - true if enabled. - - - - - Writes a log entry. - - The type of the object to be written. - Entry will be written on this level. - Id of the event. - The entry to be written. Can be also an object. - The exception related to this entry. - Function to create a message of the and . - - - - - Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - - - - - Gets the serializer. - - - - - Enable to replace the default serialization - - - - - Serialize item. - - - The item. - - - - - Deserialize data. - - - The serialized data. - - - - - Unique name which represent the correlation - between the producer and consumer interceptor. - It's recommended to use URL format. - - - - - Json serializer (this is the default serializer) - - - - - - Adds the event consumer telemetry source (will result in tracing the consumer). - - The builder. - - - - - Adds standard open-telemetry tags (for redis). - - The meta. - The activity. - - - diff --git a/Weknow.EventSource.Backbone.Contracts/icon.png b/Weknow.EventSource.Backbone.Contracts/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 + + + Exe + Debug;Release;Gen + True + + + + + + + + + + + + + + + + + + diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/ISample.cs b/src-gen/EventSource.Backbone.SrcGen.Playground/ISample.cs similarity index 88% rename from src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/ISample.cs rename to src-gen/EventSource.Backbone.SrcGen.Playground/ISample.cs index 427f1192..6e67c905 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/ISample.cs +++ b/src-gen/EventSource.Backbone.SrcGen.Playground/ISample.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.SrcGen.Playground +namespace EventSource.Backbone.SrcGen.Playground { /// /// Some doc diff --git a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs b/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs similarity index 72% rename from Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs rename to src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs index 57c06983..a0fcc466 100644 --- a/Tests/Weknow.EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs +++ b/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities; +namespace EventSource.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs b/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs similarity index 70% rename from src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs rename to src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs index d8665008..deb6394a 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs +++ b/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities; +namespace EventSource.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs b/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs similarity index 74% rename from src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs rename to src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs index 33f3d379..81823de0 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs +++ b/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.UnitTests.Entities; +namespace EventSource.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Program.cs b/src-gen/EventSource.Backbone.SrcGen.Playground/Program.cs similarity index 72% rename from src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Program.cs rename to src-gen/EventSource.Backbone.SrcGen.Playground/Program.cs index 15022f2b..f1ae3e73 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/Program.cs +++ b/src-gen/EventSource.Backbone.SrcGen.Playground/Program.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.SrcGen.Playground +namespace EventSource.Backbone.SrcGen.Playground { internal class Program { diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/icon.png b/src-gen/EventSource.Backbone.SrcGen.Playground/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 zukK};Penable true false - Code generator for Weknow.EventSource.Backbone + Code generator for EventSource.Backbone Debug;Release;Gen diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs similarity index 98% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs rename to src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index a3b69db2..3b0c47b0 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -4,10 +4,10 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Weknow.EventSource.Backbone.SrcGen.Generators.Entities; -using Weknow.EventSource.Backbone.SrcGen.Generators.EntitiesAndHelpers; +using EventSource.Backbone.SrcGen.Generators.Entities; +using EventSource.Backbone.SrcGen.Generators.EntitiesAndHelpers; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { [Generator] internal class BridgeIncrementalGenerator : GeneratorIncrementalBase @@ -163,7 +163,7 @@ protected GenInstruction OnGenerateConsumerBridge( builder.AppendLine("\t\t/// The target."); builder.AppendLine($"\t\tpublic {fileName}({interfaceName} target)"); builder.AppendLine("\t\t{"); - builder.AppendLine("\t\t\t_targets = target.ToYield();"); + builder.AppendLine("\t\t\t_targets = target.ToEnumerable();"); builder.AppendLine("\t\t}"); builder.AppendLine(); @@ -323,7 +323,7 @@ protected string OnGenerateProducer( string interfaceName) { var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; - builder.AppendLine("\tusing Weknow.EventSource.Backbone.Building;"); + builder.AppendLine("\tusing EventSource.Backbone.Building;"); // CopyDocumentation(builder, kind, item, "\t"); string prefix = interfaceName.StartsWith("I") && diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs similarity index 95% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs rename to src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 43c59947..b67669ef 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -4,11 +4,11 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Weknow.EventSource.Backbone.SrcGen.Generators.Entities; +using EventSource.Backbone.SrcGen.Generators.Entities; -using static Weknow.EventSource.Backbone.Helper; +using static EventSource.Backbone.Helper; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { [Generator] internal class ContractncrementalGenerator : GeneratorIncrementalBase diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs b/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs similarity index 98% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs rename to src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs index 09142323..9264163b 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs @@ -4,11 +4,11 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Weknow.EventSource.Backbone.SrcGen.Generators.Entities; +using EventSource.Backbone.SrcGen.Generators.Entities; -using static Weknow.EventSource.Backbone.Helper; +using static EventSource.Backbone.Helper; -namespace Weknow.EventSource.Backbone.SrcGen.Generators.EntitiesAndHelpers +namespace EventSource.Backbone.SrcGen.Generators.EntitiesAndHelpers { internal static class EntityGenerator { diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs b/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs similarity index 91% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs rename to src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs index 844c44eb..fbaea755 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.SrcGen.Generators.Entities +namespace EventSource.Backbone.SrcGen.Generators.Entities { internal class GenInstruction { diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs b/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs similarity index 57% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs rename to src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs index 4492db8d..cd4e80ef 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs @@ -1,4 +1,4 @@ -namespace Weknow.EventSource.Backbone.SrcGen.Generators.Entities +namespace EventSource.Backbone.SrcGen.Generators.Entities { internal enum KindFilter { diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs b/src-gen/EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs similarity index 96% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs rename to src-gen/EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs index 8685addc..d6714d0d 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs @@ -5,11 +5,11 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Weknow.EventSource.Backbone.SrcGen.Generators.Entities; +using EventSource.Backbone.SrcGen.Generators.Entities; -using static Weknow.EventSource.Backbone.Helper; +using static EventSource.Backbone.Helper; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { internal abstract class GeneratorIncrementalBase : IIncrementalGenerator @@ -22,8 +22,8 @@ internal abstract class GeneratorIncrementalBase : IIncrementalGenerator "System.Collections.Generic", "System.Threading.Tasks", "System.CodeDom.Compiler", - "Weknow.EventSource.Backbone", - "Weknow.EventSource.Backbone.Building" }.Select(u => $"using {u};").ToArray(); + "EventSource.Backbone", + "EventSource.Backbone.Building" }.Select(u => $"using {u};").ToArray(); private readonly ImmutableHashSet _targetAttribute = ImmutableHashSet.Empty; #region Ctor @@ -196,7 +196,7 @@ public void GenerateSingle( { builder.AppendLine(u); } - builder.AppendLine($"namespace {overrideNS ?? "Weknow.EventSource.Backbone"}"); + builder.AppendLine($"namespace {overrideNS ?? "EventSource.Backbone"}"); builder.AppendLine("{"); builder.AppendLine(content); builder.AppendLine("}"); diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Helper.cs b/src-gen/EventSource.Backbone.SrcGen/Helper.cs similarity index 98% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Helper.cs rename to src-gen/EventSource.Backbone.SrcGen/Helper.cs index ee3c0258..e2ed8c7a 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/Helper.cs +++ b/src-gen/EventSource.Backbone.SrcGen/Helper.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -namespace Weknow.EventSource.Backbone +namespace EventSource.Backbone { ///

/// diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/Properties/launchSettings.json b/src-gen/EventSource.Backbone.SrcGen/Properties/launchSettings.json similarity index 100% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/Properties/launchSettings.json rename to src-gen/EventSource.Backbone.SrcGen/Properties/launchSettings.json diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/RoslynHelper.cs b/src-gen/EventSource.Backbone.SrcGen/RoslynHelper.cs similarity index 100% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/RoslynHelper.cs rename to src-gen/EventSource.Backbone.SrcGen/RoslynHelper.cs diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs b/src-gen/EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs similarity index 99% rename from src-gen/Weknow.EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs rename to src-gen/EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs index 21f00319..5ac601c1 100644 --- a/src-gen/Weknow.EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs +++ b/src-gen/EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Weknow.EventSource.Backbone; +namespace EventSource.Backbone; ///// ///// Syntax Receiver Result Extensions diff --git a/src-gen/EventSource.Backbone.SrcGen/icon.png b/src-gen/EventSource.Backbone.SrcGen/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 zcmeFa2UJx_v@LoNMMOkIRFohfIp-WCNX|J+Iv_dctRRvEB#8*9BqisJAPNWw2na|{ z0s;a85|s4U(QexA+xI>1yYK%u-XAjBcK1G2wQJ9+T5GPk7N(82&OBjKWOe5!mbDemZF82n3B>BJ7+tm8+P`j z(qdwy_KtRD7S>1v!hI-N)m%+={=DGU*cVaRJN~hA%AEe#&qPViOx$XD)jauu>1qHLg*^fnv&59lY3~XeV%t|hfS$bJ z_;UoBd+$k1M1y=%0}D~`!RZCjL_IYBK(q$uXdYQie|*GM&%2_Th^rE4{>k^LR1uGk zA`IJ2jHeMwj0i(&zu8`dfAV&s7aBr0mh2qbqi6)_S>zpY#0`E#ZkI;rHG~!m0^dyb z6))mFBZ5U*(@Y9cSb=Eh#64Mzz{EzdDBlU9MI7})7{0!8#T^lth`^Uv(-c^x&&Qpk z1Edm*1*#}`uLbC0GCCaB)MO%|>6E@ekI!Xv#OT3Qrbds%i);ZLrzh6mAP|qD&%xWS z@40v4Ke6a`s|xGUT@Icv;OKUu)DP} z@qCIRfK5L@^62(NQh42I3SfD=bWUSpm$A{)AcW*n+@!_P3sS$~Yl$M;9!h5Nwq(l@PXNFF2pFj^(ZlorUc`92W{Xx`&;l>+79r)N1_9`JVDIxj7ok@qC7_tK3EH?}MaJ~~&Q8I2=RNO-H+ z(>^cxG2tWn7dPxvx6j>bxnoXqF^S+ff%!!@f`a>UnHPJ*F1Dz#aI-GnpG!QQXs514 zS5D`BF1igXP9}~jj*yO7?OAqa_E`2w^>y`jwa8}%sy_J=>g=l1&rZCH&r#1#R$5dg zR5gFb|1>z;{HdLqaJsMdUI>QvOSzYdV(H3{imp%BHj|z-xme=PKxTC+=eA1#=PRnB zjzMo%x|;iY#5G}p+0P#Him}VEYnG_2$C?QrpT6&~{m@zA#2igl!n$wh82$?F6fI#U zhg=maQqL+oJ|SK!M=D1-M{==_D}N~~IkWCsjrL9ej!26PwH~#kd1U9Y1Md>i>p*;W~Zk88x;>TR=@@25OB@K2Gm%Np~joBKxO!@Q@qXY(ygU4rLLo2B9WJzN7-MKSCe%nt4bk8At$BZ{AJ;4d29Kzii?Fa z?=gn+%WcYijLeNMm{&CAzg&)~i}9)UiP#f7ekynnCx=*v*w?Al5uZl*b1hpe2alw_;Yg)T(Pnpg`|`Eegz`+c@X6FT7OU}GUqvy+ z0Q0iBE6gU$(VBS!g%ufY*;_^{7lby298;%-rUkgi*v7Q4zu|7EcwN!1_|h)rdeFF- z-<1Q?1J8qQM3z4#nmKw%fJlJuk)3T%Z?gKk!hFNX&Eml&k?==VBIiURZ|2>cd67qU zC97C1plz90MF9ygjb3VZ2`O&t^*qf_P6~a;(wmaD;VABhE2#%dc#U|I_sTPF%W5(bYt7c>TJjbPICuX%-%2VDdz~DvO%@<{8R*NUqv1WpN7Don_^cA(3 zwa3ehzTHkH6gn3YnvXwxA>o|gdzumV8Ny(@w)(`8*wSY?uc}PD%zDh`XU7;*baCRd z6J`@|Y)=gim7W^cC|%mvyf)07&_Od?bE{meDy?3ivKE=u+S$P`td_3!MeUm!*K*Hj znbE+uZTB*vSM4Upr@mp`+O0jI*5||OXzKEPW_`#V>79}-;rx*7`R(`}8~r>58;7M9 z^d9uR<5zIX2ddun5%&nHsYLeEFG}LHTzFbE$@RM?{Ig`$D%hPr%)ws@+J=YGx=J;#*JN4Eoo!5$o zXU=L?YCb67yKb}Kydbb2)2rB0^RVVYewCh8)v{+@nU~qtJYfoV=jfL@kJa-B{e3CC zf}?foo_1SJTN~r&M|~Zs_d45a2HmKa^J*1quYb9^Br+&A8r&zWyFc=NWyH_2!R^s{ z_lV>cM$oOU{pgLz(_+UW6C>MipSr!6!Xw1XJ0cjVQ!_pl zafv@32md8Ve#6<>o{yQ?&CQL;jh)HP(VUr;mzS5Bg^ihwjS*TfI(gVS8@V&uI$ilO z$e-hgBb`hfE$p2w>}*L<;~E*;xi|}wlcOGV`18km+1MXG$kyo(>;Oe(cO!ddRwfqa zf9qswa@fb-#nJlv)J#p7k=95Xq^+|P^ke* zwXylvM>{!Ra|HIN-pN@7`Pb_F!cfrs zUk7rwF#Au&L4ER{f`D|l_)of_KKaq@`=0u-)B*q+pO_=k$l1bD-eS? z47MjXDUGIut*M=x6RiOAKfC<3asC<&DQ@J96hdumMiy>HHXb$D)okz&7d;C*9}CM* zgZyauk1^!!OfAej{v3mwk(CwxQDbB0W98&yVW($dI~?Pumj4_B4x*`%v(Yb&@^jmt z#xXVFGqZEFF>)5NurV@6GTYml3o!q6^QV@F+mlbs&f3lqriBz@7hwLk+yB}N0$!yb z@ve}Qiliv1^ffUqR$eYHMm8qapNILW-M@|_ZfD|xI%gubCP*h(RHvU78LBCf6W9n()YW6f5y*m@IRU%Y6Hp3^GVw}IUB(XNQ-};j|I$!gPq5iox_Zk(U{AO zosol!gM*Qmjn|Zso731B$%5qNMjG>e-&KF_{@2X@82Znr!_YrWwyB*74FA_UaUj`Q zS$Q~(8BMs^IAPZzx#69;k&LEX#%yfH9NcDX#yo#M=%>N{!-EtZEx@A~S^w1LPi#3k z%~&~EdAJyP*iDTYSxq^N8F|^^Ih?%QoE#j+yd0)%KTZg|%fAfsA08!Tfm&&ge=GxP zZB&sCf4}?JN`JTg_C}6QNR(w0B>&U29XbnWdiW#o-9qvinV`IekO|6OAWa3B|91DU zY5lXC#eZDRUxVPs_&;_3&!6FT18Ms|vNu15`rhh4AH>Pd%-PMz5h-F0C+Js9;g3=O z-tC{m|NAKcjJ}^mYm5KRLfF~5A|3xLO92aR1PlcUcEQZi&W7}dSwq@8nUOl%ks8_C zTU(eIp%$6h)zB}1 zF=aI3LYlH7joCQaS&jdE#sAWtJM>R%V5ayuIsWud|L67`YIps3y2%YATXUqT5c8jJ z_UDcNn^w+<+t`Smh1HDF#Eg{%JQ4>xqcJxRFC!}(4;MGLnVAt6JKLY{_rJDsz${q# z*m!=g+&?V+f7`(PPfQBR>ik5le(c%*Gxo(4>1gp^u`oZ)(&SH=NWUA^|9jr~zim@_ zOt_7VOgXt3O<7n?8Cj7io65s$#Ass534Dp$jMtP0`TJ!4zrex%+bj2Lg!;#U{9m@I zKSyi-Kk$Y>sNCO4^`Ei8eeBe>!La zD47HmJ^_{g&S-@oj{Xn#ej4rjXIiK*$lB$5n8VKU{fC(q_17PLfByWReYO85eSiM^ zpMC%8s%~rHECg)k`*i+s`=?$gHRp%apb82m67_Qby!Fr4N*3-&Yb|jLkO!PlVH~{Q zAD#aB$-nm0{;4M$_s>0l`s9zEKMu-IO5^v1`EeI?Ops8S4>i=E*6c8F{NI1^*DL++ zANz2`-w!#2;#V$y!}TjD9k%%m*C7sL@ZZ1WqgLnwab;x}Bsg3@7|-*6p5@hca<;rbPn4%_^O>kx`xx%ds&ub_0; z<~Ll2Q2ffpZ@7L1rNcJA;W~ukS1x|T^(!bHw)qX$Ar!xI@f)sRLFur~Z@3Pj_?3&_ zaQzBOhi!htbqK|;T>OUXS5P`^^Bb;1D1PPQH(bAh(qWt5a2-PND;K}v`W2K8+x&*> z5Q<;9_zl;upmf;gH(ZBM{K~~|xPAqt!#2O+I)vg^E`G!HD<~bd`3=`06u)xu8?IkL z>9EajxDKKCm5bkS{R&ElZGOXb2*s~l{14&6`s-UskhbtuByRAXBe&ci)WY|bkeW!T z$RiM*ln8|XEd=800sK9WK)A3V5DSI~1mArG;)311>rD~}M7)u-xQLqj(5GZKccQ+& z17SBW)s@pnDtRIV@E}4?C>zj9w!9a zHZ-dZ`$yC;x@|Bm<(K+w?eD+stSw2?*XAmZe)v_WOQ>hL6MjZ^lASsQ=ez=A@z61_ z2?$QHT+C|lM?=Rjy2xVK;4h*DH(4;-QFpK#+29V^twOkCh#QZ(LlB7kt1&49>dxO9 z|LFd=#y`3@{H^iN&-$bB&(Hd!@z2ltmx=vXp4D(Cu!-l`%a&S?b)5PAhYHmEuAd&2 zm7ODdRVb8ZO`Sm$H>ZRd5Q|499v|y?UWq=iP>R+K?J~Oj(9+#64)b?eXE>KU281qm z41BoSRkEf#y;=#6-@n3wzMUDnCjJP+-jb?QXhLivwV~dB8J+OY8p*LCf8vH3(K`}kuM8%H z3~L>}iw3&tRss1e0X#hofve4+jgz3^Nm0gc98 zc^O)Nx`}%KWNE9nCVIv%DLGiewHl>@W7pOjda;y|p$RuIMfQ9yEiRrR6EBKg^Sj$X zR^uPnP&DMkm_ZRO^|a+F??D2>9G#jK_ZB;>N`P|cs_2~-4%2h_+1Zx-)8XuN@>DUe z+uF$K=})G1N&5H*y12M-aB#fs>*J(BFlFo};~HQ2{0$9n7H#yn|2dU56VCA@*UQxt zUR;G5m=3kEj&Tly@K!8XWEs0*xQ*FzpPxK=@_NPZz)RQ0CJ)~3%hpzj%c92Sr@>pT zrp9mXvj)C2s;;TguCOH@F1Hbr328M|+1!%En34&e);c4advta&?}bHgW|94*C>PC74$HFw}>1&-1te1yLj9zAy)Tn?=8rTdarkAb8>QO zdu@I)8h`GSt5X)*->*T0b6g?oY)orltLc-%!l?Bw;ndSY+x`RB{gTDWA6MRq$%q|5 zujj{}+dMWQ&ZwO1z1FJdv(fh^ng2p$ix>+5#ifWRQA?TfNtFltJ6}JhMXDK}6BVG& zc!=#xc%`2-_rN&}$w9o#9f(V(t?#|s^d^!0c)-8x%!{yP*$q~Kta;>HbDpTuU zZ`jx%=Q@)=jn*WzGu4FWe1S~QZ#;1g%5ChU1o z=zreX1N5^^%Y}h~q)sKwc# zq@USpHfC*{EW8vo;N9tpd6!iZ(;>*Sp|9`VyLWGUd)XNlnWLo)O9pA^=%%J4g{cUz zRRGF-{hC;Ls!M5`@C|v+-1bk6FtAEJWCjNZODig-*S{7H-frQDS74OENPtVp^S*`XyXlxssEUUl|w0CPX|o)Y8I`5SqHGcH&{MDA~829ax)J z)?BBPaOJ4ZuDU;T7f3PIrxL#Ihsn^BsN+V%&CT7kC!mzjHsd(y%vZ+TB9ZgtHp&pD zO<{}Pp~6^L`na-U4#(+!huK~r$&eg-(up8Zco;sOG~VRn*A8ozT^0f)nrGEcpP#hX z@~W#2SGKAQ#E>|?P#RO2k#Q`mmpAu)T>PUwUtv+qsa5wj^B`PSN<_*$D|W611F!$> z+ZZFZTOxb!KYg+WIB(pz@%H&{04p}QDi*%n7Z*xR-@bqUy40fgHiK_JUfif{R(<{D zf`PCOvn%*m8Quyq7km4^W_V$vCk5f=#M%tx>$i6%^V8DNH5wOTTU|CQxtP#?jRJ3S zZ)`4G{u*5vlcdII~Q@Yx; z>oFC$L`O35l-M=v86GcBPa1mqw7NQCvZH1tiS7alEClKG^{LOF3we5a!ng}=H%e>9 z(3ZI+p21F%xctl){j(G;Gk2ogD`Qpz@6~JNHp9)^tJAHmtz_rJCbxFx=jR`%rQPc6 zREWdW(beUh3_N?E$AP>(p0QU4d2XH|=TmKi}YZ zP_(SPURUG49`1|*=aZ=61Ch}-TOrzZgz<|{LuFz5HLgY_gN1|EM{m6l(btN#F)1R5 zi;JVJ*Trt;FXLz4=m&IJwB@;)IM;oq;)yXCEsS~yrqixsNV{ameXO zX)?T(F;_7Pg91$7K4M+KPI%iu(@b4@$#m^8^&}??ZhNRi8u)Ab#uL=(-dVU)?4oyE zCp;!ZN$oI=Q^^D0Qw44Z$Er}~%=c!Yza1XVS()86!QlI-_xkGT#tNLkT#3Z$+kne< zSNZmBek1nWk!0qK-8|RJE6;cGb@wqSggdJGfemi166oW=)ho@?84C&>&hh~^@&oa2*4)NThM zCvKFL-)2n|#Qs94_sBYnGE!AhFwW#I@9nlW8Am54Ft_PNMbQ;wo?)C;{pnHhvtDq7 zi$r8Do)LYRab2E)j?Q@Y4ICPhOP4OaFGil6wC6!ymwt#{dXg+2(Y{eHnc9m)vE{Up zp&`Wu5i)caw{zcY{MKSp?O&eaj)I-*aImqlG2%LGv%68?M_if>8%3)|F{`(=qJGaN zziRdpm4e*4qo=@XX_wrtS$Uq6mEtM0lKsFRILaJ)*G0_@RU*aaQKRkq+;(GRqp7{~ zrY(1ghvEv9XV&JY)#p>_16czpi)HMz)ipIs!0cpaXW#abv$3&x=sp$l>3z`^WQO9` zPLv~-B=IoS+5uxWT{(R>Eh|ehbqe=56?;^-(Cd5e_WQA!R-P(aDc!xE`q3-h@D&`+ z?icx9-kO-vq8ec5V1BdyudyDbrw4M6xuYkvXH>^v(R#0jT_@twMw5HM5hA?58o9t~ zA@v&Dn?1SR3WbX#ne*WMxQc|J&IiIY)KpW2Mx^l`#P24>v^L5p>|Uy~%8A$F(F_tAW=NC+r*T zdSAgV!7U=co#AnK>)6`)bn#U_c1#;qnN0$mf#Oy>K83fT%qW(U*;lq8r`hDPGIG zvz^H`<0}k|jOyx=b6u&5CMH)QIJA2>czK{=JRaKP~?flA0 z7I@-<0a;bmvo&rj3S@;89&rn21?vn}qOR|Lh*5N0DjRT|iPJ2$8y7P0*-+=Q9eKKJ z0>RgaV>_cb`I!xNaHFW{SmjqqxbzvgPaXr)v^^($z{}8>T~MG&SUu+4E5Ge6)Sss- zsi=55S}IV9CUATutE436wD4{eDwI-H;0+36eom0k-jpKfWe-%|x%(Q>s|5#(Qki#dAe%< z+hQUxj#H;jfqlF6b$xC<&(0?dWqyDp1(hz#UYe zp7jAD21bi>DS>{e!docS;D%zobwp(-@M04Zb~=T2K1F`XN%&^NT=S`TZ+~LguAUfCF)R-U7`S#$ z!Se?xDI^*N1`!=*FDLDvni7XK0cC)bk7hp=Lam~#{Gx7atbM4=ik6o4#p1@j)YPtv z<_{ky?^a=daJ$HYX@!5k`qttniK#1^iJ#0$cxP-E9jj<)X(uOxFV7~_?^4#))jf1< zqkZVmd=XGT2al$~e*(K69y_(l0|Y?sAy0IC{CUo%i5JH&e<<+l7zzGT+|Hz`iE(bN zTeio0vm`Zdd*xt%g}Ebq*=x7hSV#kep3;y{7< z#zJ3ge0)z|UlSZ0P#hpe6d7Q<6w%Y$D+VhG(XGJ7J5?|h)?9?JFNmDpuzr}Gwe)Lw zmS##szzPybIL3e;_SG^9{F_R-;Rjz+4<=up7B&T*6Mk7Z1*TeVH-5KV^=@%ppJhfx zMZ&jlo^aAx6Xh%y8u~P^M+_{^fW0>uny&oZcoMb~xS#d3%X@d+#yli_R!|EFcFYV& z%2fOHeKt5wjdSZ{85GRSkzOlx3Cz9@2e;oW7SwkFdPqcZaj{mVJ(;??x}Mvp%kBqi zVdW4wCy{+QnyMg3C@Y@tcC z#wJs?BCxse?h1Z>^)m0lJwe#mYC838vA}$?F-YKZ1G@M2WU#Y~%jAZq1xOt5(B0|F z2NTb-Qj8b-^A=W#{dQONT>8|(_zn#Zx3;%WLth9s5uhi$T$^bJgEAgfln7E#9Hngh z)BK5zf$NG64y=rfj1WYgE>1TtA^@9-+*(E*>y4e#ol-3ZnEtnK-&m45Fv;$SlUvp@ z&5y;S%`{7~Gvq2y;@v%xD{YzFJ2s{uD|@o6tgN)O6lGGwFS3Je*xvK)d%~6|2U1BO zE=&px+zks0WkwmF&)y*MeHyDv+T3&nsjewoJxA@rJL4kU5OECcv}4DPp=h0H_mgYz zw%r2*!A-`GGctk}7A$?i)H#j&Y@r-GiTeZ^>Q7sHJ1=mscP1Ka6RF;7(sANOSJ9r- z))GOm-34E%{*V_`Hc9>NcbST)l`Ajj0*VST>6PF_#n$G0alF@!I3YkWOyDjteo*zy%@zt6)nXAe%E(z@p_5PFc*p6u0z>IZLthh z1_eG=_wcZ+mlr>5de6}>u~#cy8R8QXOp3HVe)*_LZ3_Ky1DwI5Zmmepq zSK7*Ik(4d0Wf~tJf5Mqh8#s+!?J5R%iOc7W1C>Qv`(g~WVkQVvHKU!dfG@2I>Y8Pl>%>7< z9?*9qMk0~k!1HGNb?qp@8>An5x|5xk*R;4%48VJOd7)K)d=jQ+B_kc6lw0_L_XJvU zGYf3WGj>K1=R-`%UXjzz3=hR|Q*KF}XkJ`-J`_i}7nbjnf1c-B(I&{Fb`uWZEt>NTTa_)9=z zM$yXr>IFKGSVcud(ApE2=K_wCNRb%_`M0;Vp{xoVbXZsxiyn$kuKW=Z5e*HEVlFP6 z2m4#~RiF>Y`P2f>^yLV@rAU9eos{8LAr7XB3>`gvJw7(ZLH?I@LOQAhg*m;8PD}}5 zC|3e%@EM$7Y0$~Pd`W+JGH>@yz{9l1&1L)dd*(Z=QmOGyaPBoMZe-`Am^6n^+WGAy zdBd(j6X#?63f}83|1wW=1fisc#(ChDa45lX1F@U#mb^@fi%Ao>(Up}iZ8FetsJ%YC6G0dg& zdZ9N^MC(dar39aai>s@Ofx+`n&4hLg!(bdvz~(N{uY#{WHZe(4$2^0)T4;Cf^C{Yy z)=Q1iV0Nd#15U@PDv(`@zzY!vjg_1L8|BhK%YrR&^p?nx0RL)FGqyw^r(g>~Yk!)X zo1UJ2l1VLW^}!QAIK7 zKWiF7I-^=sW?toaeO6X5xMm-@f-~E>lS>4FoY_&fPr}9F1cF{Szp(HmH}}&ht!ifN z@cZ{qo6YalPjXFAKX4DW8!ELBgs{&^{o1u-x)ruaU~;Hn?Gz!`+i_GJLxvY($d(8x z5J&-V=2K))5Mbls1fLN#tQamDUV4)vnAEGl9|71*ZFs6D$ggZ7$us3_4Xc;F1j2iQ zVKxFo(30vpWT99G1lK7&HvWUF<_R*ofZzbxK-__oWOwCD4zdEmGGbXxFdz{)!J-`i zx+f^HYinyaY;85pUW;)@pgtYUeqUubmq%q;2-IXfspKEL z3OsQZC=A4iQ_g&S6e`G@!7q)#{s4q1&7M)WywG(?XUu*26wt#r3D3V!T3TA79+3Cg z`9sWOL(izBr#3>-w6m0fk8*OXMylpgbT?L8E(?PW?jh}1BVd>qyLNP<`$L+Xj0Y9V zSxi~l%V$Nst;3(96L{Ue$mdP8EWqF+hoqhwTAcP>PQlSC?7$Tc9lbX8jS_U;&Q?{xGrAg6y z(hW7nfTn4O=J1OzfoHGm_Z9e-Vct(8t6o94A&VCzWIwU#M^`~;XGHuZ`+c-1c{C}c z6(E=d4YZHo>;#@@E}rO{ch9G1V93V2Rs>OTF$BF}5nfHN zzJ<7ub3pGFFpZbDPci70hl7&$8Tcq>R`0U~e+!^5lIrSx=}w)z)Y}ucPFqq&6(~b~ z-N=Zz?0i4XizVrkqGUO)q?M+v_p%{Xg$2Pmgw3rp`{9Wn6EXQl>Bg1%%>_sxha^jh z>!T3&q@DyP*kcX~1t9)`SVAs|D*|=0QL3U8MWfsEbz+o0+sVHou>72Um-vzeHbR5SI!DxbEavon9`<3}Z=+GfRCVy53D+KkjZ z^>c$u+V%3_MSzV@0sW@}whsvcusS)uZotVv=`-%jRtJNrkY@)#$XQrNx9Wt7lS9a1 zaPW<{!A&x(9dg70X&u$osxlS>a<%;j-wF;KXXoZF4}c}sA)W8R>e*|SGtHwenXC*F z(`If9O@ZtJBz%c-%5!NhMX;H@AplGG{<#l-3$fcnop3=lJ$VmCe5_}b607cdWOnc_E-4G)znyNi}BVBhDYrMM(55U-hlO?uC7uCsjmC^0-8d7ir+O9b+PJZwV&- z>e9lxx={|IFjjqQ+FnOLzO_r1sfkm1^LOlMZiY*wp&a(eNNzsna>2e*00deE1*hxB z3xws;l(QixOYa`2eDdLA%0vi)u_}fc@%!G$(LE8n`@d87&wxEWFEe0WmkOP{(NW#%?B za#m$4>g7zi-MO#J4(v%l2Txb=b9|; z`dk<47{t*C-5+`4952h)Go0E~R?pMBpUbecrJbk2$Hxc!Op77DG~cS)T?d#B#5Uev zx+TA@co^YpccT>fr}ihPrD(HmF1*t#{NS~M)*h~Gi%OysYrlD`BLJZf=nKy_JjJS< zbwpcBN0mHoh&>B28|t#3Hu+{_!$k;I1!#p?=fh{^$(I+*&PKM-($k}awd}^Hvpjyr zBRl{aL++JGM@DJ66oHGuFCTNDjWQQ~G|@>EDiNlgetd^X-`mR1fpdu!a(Sr9fQkVX z1E}~uKR-YG=(D3A<9u&{#%3?`Nk+zl8GZCNs$NG+OOoK+kV!{-yDTk>P!vJ8677-1 z5njO)IO)v#XLZ`Rw6@_{qUsIH#J)EMS``sk7W&Psi%px5S&t%~#8%{K_V#I&DZ zIU$kbiGJMp*sO(gMq#0Sl%+@}MfY9?VS0({msc@&M|#hsx~7UD-!8T?kVLAgsl7UW z+54)U9W$!50BD=oJzMhVM{w5T=rxNSXJnRkd>TB`0&+_u>`>cRm<<9GRfj)}ZJ6@koy7a^gFsUYhN z6dv?ykjJ>53PY`f=p*IfQsZ<%{Nn6q&t5=u8;CSKP>DbFE z8Ks$-x9{G?K?MS3G<;w8x&1XI*2>iKTcF~BmWDQKwUKZx`vYIa@bx1fzb2)Is`f&aaULQnetyE>moH)zg<9L0>%(zxu1>e8>gdS8 zsX-+J2)V2c$7($+$ySwCv*eO3O7pcK$^nuCnP(A^fCud7aDvO21`G8%pjzeSWP@#m z)7Ge42~^?$eQ8-9tvQZdmZb$49@W()qasu5(Q0+ST<|U9#HTJ4!zKegAbgJq7C&lJ z!Msc-xD-o*fG@W7Vu(0I%%&ri4iHkVf*(`T(t6!Wotlf+vbi)It-|KDX?^p>L{whs z7hBzGf|yoQa(@0KR1Ph{!1DwxC1{CnlDH!wYiLq3_@%!yiuOz!)^#M3vVM1l83Zeo zL;^ZR`cod7RA#8IC>t=ChAdE8VWHB>b6>~^ns9_y0jteaNX~AalQ(@QJXDy}gEC8qVt6e?PI598)6PZDnZ~<365DueL5|aHs?yie8{- z1|;c~wX~$buPbE5fINp%jgG$6H@Jt>BLYLg*q-2+o*kKlk zi%Jwg0R&^wbIwb8BL2Vxlr6L`GEHIz;E9tkIxx`AKuOG@Dwfri3Gr8nagKw6J+#;CAD3p73tmf)DWUvp9xB7n0?GvZ*U)(% znx0E!ZcSPK!*SF4koAJNm@rrV$y~TDZQhNYu|xvGL(B9nV?&)MYHUj< zp;Qi%4lhAw+@9sGZv|rl@vxW5kn8*j1>Tjm=T44}T2PfiOiVm9?d!zxssI)q{e3q! zL=U2mHdIlMyzj;oJPK0NY~uUCD$y5JX#>$=tl>z&>d{ayiyce7Th(JKXX@NZKs=L6&TfM;-M;lUO zS0JK9fc?-fBB{j8lUvmiGr}bOAd<1lwHNi&! zU3G(r9Eq(3o=JGnq;J3jgLHQ&R&goF$uY0}#76EAMUBU+f2iq;eJj<2S zmCMzO?>J5dDaC=h1hg9u8v~rn1f;3X!n&b~4YK1h-ks;-;<7xw*w>AF_MQqDq`RTK z5J}Kb<}1;pQz@C5o0ou5Yn=QXO_66iK{a6z(<&}NGUs=&<@a^7qCVo~b;Los z0xru*t0p5&gWovg2x=WT9LUYBLvE=AXqNBiTbDs(&ZerX9<_D-^#1V*VsBNdLJNQK z?euDRn+nmb!nka6qKSM!B za$}=t-vj0J8~8VsN#?6NX|zAeDCvhx(&nd(+ESw`pUS_i=A!I5vOQiIVr@m=wX0w9%g;ex*N8E`gG3j~>=n0u`S z>T0hkY^12V}*8#U@)| zN*q)%Cn8&JK?MrXW>o5Y>IH_F;Svl2rgh@LE{IfzqGW3N_BL1i

9`^J#0udjOm`|0!d09o6%f)G*< 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 - - - Exe - Debug;Release;Gen - True - - - - - - - - - - - - - - - - - - diff --git a/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/icon.png b/src-gen/Weknow.EventSource.Backbone.SrcGen.Playground/icon.png deleted file mode 100644 index d6811ad8bcf3ea148ea3d1c712601c832e8a1b63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 zcmcInX;c$e8%7Z#2qKEeP7GAal6`^1^ydax@nT7KmAx!LkYrTEJsrwo*9+j>H8D5qLyP zA>ZhYeqeMcNatbJ{RhodFc1O}BnQw6kx(pSC|H<}ybNTloW^6&A0hHk7RE_w5FNnr zM!UdL2u;P&upp5_McdPHL>iewr`w@P1fm0;;D9HRu|yh!LST?c=-CT{Sd;Sj3?H`J ztS#h?g$a?%B@8@1GBOeuNyfp_U_6mdr{f7EJc)!w5Lj81SPm$#VwuGp0~?ZoQh`J+ zfW>GfBfy2j2Kr%R73PP?CkXUXpXN(8V@0Em0g&#fTfp|y=i4d3!5k{OhB?*D$uq*`r z2S4VQ=e?0IT%-^nhoycnESyV}_Z$n_#YLGII{Hfv01CuPYHOve*_;Rrh-3zZVoM+~2!u~i4pIy}Kn{E!80HE1QJ(|la2TFqnH&&}-rEx;HSSkXLViAadUBGs&RpZ zu#^MykeHMII`8S?;w^>w0wJ;>^Ko}Zd%C*Nh;$kaOTrPAwU5X^-1QL1kVHq#o+e)? zY<46Rpg)!<0|1pLgM|T=B?0j;v(tjV3G?SU^ocey1VTvvrAj`*WH4VI2}mKQU?e4< zSBm(5@<0Ye{JZ|oE0h1X{@DP7A%Hj-LK-d}qx1-`Y)zliga6;meO&uX8=He8y-_)w zZ_3Ead|QXa2(=Vx+9W%@VB}kH*OTq!r}**hNzOq%SG~J?{F|DZSY>PN$}AMBmbFQ% zb+z}S=ut#7rfk^ugn`CzcSgc+e|?At<>GMV(}dwl4ZQk_UzV{ci>s=R-CkODXF%St zH6yA2DL&#xV8d@X^SX7%>)scPKYBF!^{wcfCqc&?epfS5UAk~8J~&PgWMbe|qaUvp zq(8A@LUqE_AunO5$E`olt^Pq(YNcVUiDKoGwU%|Ex3_g_-CrHy@l64NggWN%(?YAb ztMTn;yUWz=WV^J)MXO_+yW z+D#!;Prv$BuJ%>gvsaR(3T&Om`K2DY(>huodPD^pnfj&e`@|?=)($u8FBSP*U8ecV zqx*E1h}YaaHAd&rJB`(+dpN6%sm1jM7Yx5sTYq-_0vo#ooL03eU%srobt}69b#MQq zzc{Qq$au*UetJxZk)Jg?<1T7?L&syMiP0OekEh7VBfatHLwd&UPpaEzd}9N?{MKgE z>Ai%(8UsbxEoPS)%F?g{{~~#$g}bV!{c5>}_ct2U^7=wU>5kiUz`y!hN9^QbX0>s` z@gmWQ;ZA*ja-bI`*&r#bw6M}<)f(fI1s<{(kT7Z_FN@MMG}Tm-LpBXJyisF?ZH|Se zABaBB3x91MY+Lty!Hu1H39_=H0HZpftHH2G5LKCNghs6+`Tw~)E-Gx|>}$7-IeAL;`vd!qUu51PR~vQ&q}zn+2ti$P zjF-uPki5|1>-0l+N>te=O^u-e+zRId=iBd(woH}mx=Nsau(E0)UfO0A9<#accN(Z0 z?3-nrJnSWH#ctTW(apA5_ep4X$Wgaz4Oa_uL$(HQug}ii9ax^?y6wfKHJcI{JzX1x zB@a_?ZQh>MaE|yhov~+U-u5z8M_||5j;5FgzG=sA`iHxhF3;4j!PGeEMDEDYXcZp_ z@yRRynX+dlV=TwEU}j)u1Yh_J=gXAGH9qW}5=(`xEB#B}Eov2e9mt%b4qU()x4$VV zOKGyGimonVPc^PQvF;B>x|ZX)(7nNwfzu?@>Y6QG>2O1Hk59^A_U=B-T$=sigI@&L zYG*bXMXo=&RA4gjemu6f=rzV{+Z6$6i&O5%HMjIXye?mwagJGXYSH;C-IHfLdM~B6 zWShH%w2cu|E2;jYm}vo_o)xH!rws^f)ao#ksWivgY!fvkiF+z*^VHSsmR- z{Uyun;^Gq;{&w88eNRu@=x=td*h9gskmHYUe{x2grcKFaX>s_Ui3d^tJGXkA6+@IN;s|8DzA8(9W zFX)vwCPi}2u{|Sva&6P{&rD!>b=b*QwdW3Q$+wVjP+j#Mwk%t&*%9bMhVv$W_im&V zANu+a6ORSlt?D_YdjGq9uNMAf$voC<#G0=1&PuR3l%1D#xOa69p@BGQ7_d{nV(FV& zld+KWU*Ux@M{2u0r}_?h9B#>fm$-x2R+(S0_Q9jf{NerN4XfI&Y^klct4K5|I$f~# z;Um-QWiEbEmdaHH*X=$$zY_3jqLgX)xH`L&zVn5{t&)a`;o*teyn6}0 zT(v6^nr(-Udk?)c(vj>E$ws&AO1-|=WLqod^2?LwS~AyZblq#4R9hWgVzcSUyCK{c zH%E+hBqf1XAD+A2yOO%S++V*`aapc&NLqHY=B38j$EgFW2Jn1I)7Q<`sZ;m88`;xz zy330-=<33;*EHAhJ7=YWe#}r$?v4|CbC_L=hjeij<$?KD3=_jDg(5ag>T>aZdBV98 zih;OW|2nF-|IJszr*%!!LERztLz+W&#fM(L9$Dd7l-Q~=P!hX)#%ADlkCkbnJ^ydax@nT7KmAx!LkYrTEJsrwo*9+j>H8D5qLyP zA>ZhYeqeMcNatbJ{RhodFc1O}BnQw6kx(pSC|H<}ybNTloW^6&A0hHk7RE_w5FNnr zM!UdL2u;P&upp5_McdPHL>iewr`w@P1fm0;;D9HRu|yh!LST?c=-CT{Sd;Sj3?H`J ztS#h?g$a?%B@8@1GBOeuNyfp_U_6mdr{f7EJc)!w5Lj81SPm$#VwuGp0~?ZoQh`J+ zfW>GfBfy2j2Kr%R73PP?CkXUXpXN(8V@0Em0g&#fTfp|y=i4d3!5k{OhB?*D$uq*`r z2S4VQ=e?0IT%-^nhoycnESyV}_Z$n_#YLGII{Hfv01CuPYHOve*_;Rrh-3zZVoM+~2!u~i4pIy}Kn{E!80HE1QJ(|la2TFqnH&&}-rEx;HSSkXLViAadUBGs&RpZ zu#^MykeHMII`8S?;w^>w0wJ;>^Ko}Zd%C*Nh;$kaOTrPAwU5X^-1QL1kVHq#o+e)? zY<46Rpg)!<0|1pLgM|T=B?0j;v(tjV3G?SU^ocey1VTvvrAj`*WH4VI2}mKQU?e4< zSBm(5@<0Ye{JZ|oE0h1X{@DP7A%Hj-LK-d}qx1-`Y)zliga6;meO&uX8=He8y-_)w zZ_3Ead|QXa2(=Vx+9W%@VB}kH*OTq!r}**hNzOq%SG~J?{F|DZSY>PN$}AMBmbFQ% zb+z}S=ut#7rfk^ugn`CzcSgc+e|?At<>GMV(}dwl4ZQk_UzV{ci>s=R-CkODXF%St zH6yA2DL&#xV8d@X^SX7%>)scPKYBF!^{wcfCqc&?epfS5UAk~8J~&PgWMbe|qaUvp zq(8A@LUqE_AunO5$E`olt^Pq(YNcVUiDKoGwU%|Ex3_g_-CrHy@l64NggWN%(?YAb ztMTn;yUWz=WV^J)MXO_+yW z+D#!;Prv$BuJ%>gvsaR(3T&Om`K2DY(>huodPD^pnfj&e`@|?=)($u8FBSP*U8ecV zqx*E1h}YaaHAd&rJB`(+dpN6%sm1jM7Yx5sTYq-_0vo#ooL03eU%srobt}69b#MQq zzc{Qq$au*UetJxZk)Jg?<1T7?L&syMiP0OekEh7VBfatHLwd&UPpaEzd}9N?{MKgE z>Ai%(8UsbxEoPS)%F?g{{~~#$g}bV!{c5>}_ct2U^7=wU>5kiUz`y!hN9^QbX0>s` z@gmWQ;ZA*ja-bI`*&r#bw6M}<)f(fI1s<{(kT7Z_FN@MMG}Tm-LpBXJyisF?ZH|Se zABaBB3x91MY+Lty!Hu1H39_=H0HZpftHH2G5LKCNghs6+`Tw~)E-Gx|>}$7-IeAL;`vd!qUu51PR~vQ&q}zn+2ti$P zjF-uPki5|1>-0l+N>te=O^u-e+zRId=iBd(woH}mx=Nsau(E0)UfO0A9<#accN(Z0 z?3-nrJnSWH#ctTW(apA5_ep4X$Wgaz4Oa_uL$(HQug}ii9ax^?y6wfKHJcI{JzX1x zB@a_?ZQh>MaE|yhov~+U-u5z8M_||5j;5FgzG=sA`iHxhF3;4j!PGeEMDEDYXcZp_ z@yRRynX+dlV=TwEU}j)u1Yh_J=gXAGH9qW}5=(`xEB#B}Eov2e9mt%b4qU()x4$VV zOKGyGimonVPc^PQvF;B>x|ZX)(7nNwfzu?@>Y6QG>2O1Hk59^A_U=B-T$=sigI@&L zYG*bXMXo=&RA4gjemu6f=rzV{+Z6$6i&O5%HMjIXye?mwagJGXYSH;C-IHfLdM~B6 zWShH%w2cu|E2;jYm}vo_o)xH!rws^f)ao#ksWivgY!fvkiF+z*^VHSsmR- z{Uyun;^Gq;{&w88eNRu@=x=td*h9gskmHYUe{x2grcKFaX>s_Ui3d^tJGXkA6+@IN;s|8DzA8(9W zFX)vwCPi}2u{|Sva&6P{&rD!>b=b*QwdW3Q$+wVjP+j#Mwk%t&*%9bMhVv$W_im&V zANu+a6ORSlt?D_YdjGq9uNMAf$voC<#G0=1&PuR3l%1D#xOa69p@BGQ7_d{nV(FV& zld+KWU*Ux@M{2u0r}_?h9B#>fm$-x2R+(S0_Q9jf{NerN4XfI&Y^klct4K5|I$f~# z;Um-QWiEbEmdaHH*X=$$zY_3jqLgX)xH`L&zVn5{t&)a`;o*teyn6}0 zT(v6^nr(-Udk?)c(vj>E$ws&AO1-|=WLqod^2?LwS~AyZblq#4R9hWgVzcSUyCK{c zH%E+hBqf1XAD+A2yOO%S++V*`aapc&NLqHY=B38j$EgFW2Jn1I)7Q<`sZ;m88`;xz zy330-=<33;*EHAhJ7=YWe#}r$?v4|CbC_L=hjeij<$?KD3=_jDg(5ag>T>aZdBV98 zih;OW|2nF-|IJszr*%!!LERztLz+W&#fM(L9$Dd7l-Q~=P!hX)#%ADlkCkbnJ Date: Sun, 21 May 2023 08:59:54 +0300 Subject: [PATCH 003/178] rfc: re-module pass tests --- .../RedisConsumerChannel.cs | 172 ++++++++-------- .../RedisHashStorageStrategy.cs | 2 +- .../RedisProducerChannel.cs | 10 +- .../RedisHashStorageStrategy.cs | 8 +- .../RedisTelemetryrExtensions.cs | 2 +- .../S3ProducerStorageStrategy.cs | 2 +- .../Route/IConsumerEnvironmentBuilder.cs | 4 +- .../Route/IConsumerPartitionBuilder.cs | 13 +- .../Building/Route/IConsumerShardBuilder.cs | 10 - .../Building/Route/IConsumerShardOfBuilder.cs | 22 --- .../Builder/IConsumerPlan.cs | 4 +- .../Builder/IConsumerPlanBase.cs | 19 +- .../ConsumerBase.EventSourceSubscriber.cs | 12 +- .../Builder/ConsumerBuilder.Iterator.cs | 4 +- .../Builder/ConsumerBuilder.Receiver.cs | 4 +- .../Builder/ConsumerBuilder.cs | 80 ++------ .../Builder/ConsumerPlan.cs | 77 ++------ .../Entities/Announcement/AnnouncementData.cs | 6 +- .../Entities/Announcement/Metadata.cs | 33 ++-- .../Interfaces/IPlanRoute.cs | 28 +-- .../TelemetryrExtensions.cs | 3 +- .../Building/IProducerPartitionBuilder.cs | 17 +- .../Builder/Building/IProducerShardBuilder.cs | 19 -- .../Override/IProducerOverrideBuilder.cs | 4 +- .../IProducerOverridePartitionBuilder.cs | 4 +- .../Override/IProducerOverrideShardBuilder.cs | 18 -- .../Plan/ProducerPlan.cs | 80 ++------ .../ProducerPipeline.cs | 3 +- .../Builder/ProducerBuilder.cs | 84 ++------ .../EndToEndExplicitTests.cs | 11 +- .../EndToEndStressTests.cs | 17 +- .../EndToEndTests.cs | 183 ++++++------------ .../InheritanceTests.cs | 15 +- .../MigrationReceiverTest.cs | 6 +- .../MigrationTest.cs | 6 +- .../EventSourceApiConsumerDesignTests.cs | 2 +- .../EventSourceApiProducerDesignTests.cs | 15 +- .../ConsumerBuilderTests.cs | 2 +- .../EndToEndTests.cs | 30 +-- .../ProducerBuilderTests.cs | 30 +-- .../SerializationTests.cs | 6 +- .../SourceMigrationTests.cs | 39 ++-- .../StoreStrategyTests.cs | 6 +- .../Controllers/EventSourceApiController.cs | 4 +- .../Jobs/MicroDemoJob.cs | 4 +- .../RegisterEventSourceExtensions.cs | 6 +- 46 files changed, 339 insertions(+), 787 deletions(-) delete mode 100644 Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs delete mode 100644 Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs delete mode 100644 Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs delete mode 100644 Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 2dfd3568..63c12415 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -111,16 +111,15 @@ public async ValueTask SubsribeAsync( ConsumerOptions options = plan.Options; ILogger? logger = _logger ?? plan.Logger; - logger.LogInformation("REDIS EVENT-SOURCE | SUBSCRIBE key: [{key}], consumer-group: [{consumer-group}], consumer-name: [{consumer-name}]", plan.Key(), plan.ConsumerGroup, plan.ConsumerName); + logger.LogInformation("REDIS EVENT-SOURCE | SUBSCRIBE key: [{key}], consumer-group: [{consumer-group}], consumer-name: [{consumer-name}]", plan.FullUri(), plan.ConsumerGroup, plan.ConsumerName); while (!joinCancellation.IsCancellationRequested) { try { - if (plan.Shard != string.Empty) - await SubsribeShardAsync(plan, func, options, joinCancellation); - else - await SubsribePartitionAsync(plan, func, options, joinCancellation); + await SubsribeToSingleAsync(plan, func, options, joinCancellation); + //else + // await SubsribePartitionAsync(plan, func, options, joinCancellation); if (options.FetchUntilUnixDateOrEmpty != null) break; @@ -130,19 +129,19 @@ public async ValueTask SubsribeAsync( catch (OperationCanceledException) { if (_logger == null) - Console.WriteLine($"Subscribe cancellation [{plan.Key()}] event stream (may have reach the messages limit)"); + Console.WriteLine($"Subscribe cancellation [{plan.FullUri()}] event stream (may have reach the messages limit)"); else - _logger.LogError("Subscribe cancellation [{partition}->{shard}] event stream (may have reach the messages limit)", - plan.Partition, plan.Shard); + _logger.LogError("Subscribe cancellation [{uri}] event stream (may have reach the messages limit)", + plan.Uri); joinCancellationSource.CancelSafe(); } catch (Exception ex) { if (_logger == null) - Console.WriteLine($"Fail to subscribe into the [{plan.Key()}] event stream"); + Console.WriteLine($"Fail to subscribe into the [{plan.FullUri()}] event stream"); else - _logger.LogError(ex, "Fail to subscribe into the [{partition}->{shard}] event stream", - plan.Partition, plan.Shard); + _logger.LogError(ex, "Fail to subscribe into the [{uri}] event stream", + plan.Uri); throw; } @@ -154,71 +153,71 @@ public async ValueTask SubsribeAsync( #region SubsribePartitionAsync - ///

- /// Subscribe to all shards under a partition. - /// - /// The consumer plan. - /// The function. - /// The options. - /// The cancellation token. - /// - /// When completed - /// - private async ValueTask SubsribePartitionAsync( - IConsumerPlan plan, - Func> func, - ConsumerOptions options, - CancellationToken cancellationToken) - { - var subscriptions = new Queue(); - int delay = 1; - string partition = plan.Partition; - int partitionSplit = partition.Length + 1; - while (!cancellationToken.IsCancellationRequested) - { // loop for error cases - try - { - // infinite until cancellation (return unique shareds) - var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*") - .WithCancellation(cancellationToken); - - await foreach (string key in keys) - { - string shard = key.Substring(partitionSplit); - IConsumerPlan p = plan.WithShard(shard); - // infinite task (until cancellation) - Task subscription = SubsribeShardAsync(plan, func, options, cancellationToken); - subscriptions.Enqueue(subscription); - } - - break; - } - catch (Exception ex) - { - plan.Logger.LogError(ex, "Partition subscription"); - await DelayIfRetry(); - } - } - - // run until cancellation or error - await Task.WhenAll(subscriptions); - - #region DelayIfRetry - - async Task DelayIfRetry() - { - await Task.Delay(delay, cancellationToken); - delay *= Max(delay, 2); - delay = Min(MAX_DELAY, delay); - } - - #endregion // DelayIfRetry - - } + ///// + ///// Subscribe to all shards under a partition. + ///// + ///// The consumer plan. + ///// The function. + ///// The options. + ///// The cancellation token. + ///// + ///// When completed + ///// + //private async ValueTask SubsribePartitionAsync( + // IConsumerPlan plan, + // Func> func, + // ConsumerOptions options, + // CancellationToken cancellationToken) + //{ + // var subscriptions = new Queue(); + // int delay = 1; + // string partition = plan.Uri; + // int partitionSplit = partition.Length + 1; + // while (!cancellationToken.IsCancellationRequested) + // { // loop for error cases + // try + // { + // // infinite until cancellation (return unique shareds) + // var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*") + // .WithCancellation(cancellationToken); + + // await foreach (string key in keys) + // { + // string shard = key.Substring(partitionSplit); + // IConsumerPlan p = plan.WithShard(shard); + // // infinite task (until cancellation) + // Task subscription = SubsribeToSingleAsync(plan, func, options, cancellationToken); + // subscriptions.Enqueue(subscription); + // } + + // break; + // } + // catch (Exception ex) + // { + // plan.Logger.LogError(ex, "Partition subscription"); + // await DelayIfRetry(); + // } + // } + + // // run until cancellation or error + // await Task.WhenAll(subscriptions); + + // #region DelayIfRetry + + // async Task DelayIfRetry() + // { + // await Task.Delay(delay, cancellationToken); + // delay *= Max(delay, 2); + // delay = Min(MAX_DELAY, delay); + // } + + // #endregion // DelayIfRetry + + //} #endregion // SubsribePartitionAsync - #region SubsribeShardAsync + #region SubsribeToSingleAsync /// /// Subscribe to specific shard. @@ -227,7 +226,7 @@ async Task DelayIfRetry() /// The function. /// The options. /// The cancellation token. - private async Task SubsribeShardAsync( + private async Task SubsribeToSingleAsync( IConsumerPlan plan, Func> func, ConsumerOptions options, @@ -236,7 +235,7 @@ private async Task SubsribeShardAsync( var claimingTrigger = options.ClaimingTrigger; var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - string key = plan.Key(); // $"{plan.Partition}:{plan.Shard}"; + string key = plan.FullUri(); // $"{plan.Partition}:{plan.Shard}"; bool isFirstBatchOrFailure = true; CommandFlags flags = CommandFlags.None; @@ -337,8 +336,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) MessageId = id, EventKey = eventKey, Environment = plan.Environment, - Partition = plan.Partition, - Shard = plan.Shard, + Uri = plan.Uri, Operation = operation, ProducedAt = producedAt }; @@ -374,7 +372,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) Ack.Set(ack); #region Log - _logger.LogInformation("Event Source skip consuming of event [{event-key}] because if origin is [{origin}] while the origin filter is sets to [{origin-filter}], Operation:[{operation}], Stream:[{stream}]", meta.EventKey, meta.Origin, originFilter, meta.Operation, meta.Key()); + _logger.LogInformation("Event Source skip consuming of event [{event-key}] because if origin is [{origin}] while the origin filter is sets to [{origin-filter}], Operation:[{operation}], Stream:[{stream}]", meta.EventKey, meta.Origin, originFilter, meta.Operation, meta.FullUri()); #endregion // Log continue; @@ -711,7 +709,7 @@ async Task ReleaseAsync(RedisValue[] freeTargets) { IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); - await db.StreamClaimAsync(plan.Key(), + await db.StreamClaimAsync(plan.FullUri(), plan.ConsumerGroup, RedisChannelConstants.NONE_CONSUMER, 1, @@ -730,7 +728,7 @@ await db.StreamClaimAsync(plan.Key(), #endregion // ReleaseAsync } - #endregion // SubsribeShardAsync + #endregion // SubsribeToSingleAsync #region GetByIdAsync @@ -783,8 +781,7 @@ string GetMeta(string propKey) MessageId = id, EventKey = entry.Id, Environment = plan.Environment, - Partition = plan.Partition, - Shard = plan.Shard, + Uri = plan.Uri, Operation = operation, ProducedAt = producedAt, ChannelType = channelType @@ -810,7 +807,7 @@ string GetMeta(string propKey) async Task FindAsync(EventKey entryId) { string lookForId = (string)entryId; - string key = plan.Key(); + string key = plan.FullUri(); string originId = lookForId; int len = originId.IndexOf('-'); @@ -858,7 +855,7 @@ async Task FindAsync(EventKey entryId) catch (Exception ex) { - string key = plan.Key(); + string key = plan.FullUri(); _logger.LogError(ex.FormatLazy(), "{mtd} Failed: Entry [{entryId}] from [{key}] event stream", mtdName, entryId, key); throw; @@ -908,8 +905,7 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable MessageId = id, EventKey = entry.Id, Environment = plan.Environment, - Partition = plan.Partition, - Shard = plan.Shard, + Uri = plan.Uri, Operation = operation, ProducedAt = producedAt }; @@ -938,7 +934,7 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable async IAsyncEnumerable AsyncLoop() { - string key = plan.Key(); + string uri = plan.FullUri(); int iteration = 0; RedisValue startPosition = options?.From ?? BEGIN_OF_STREAM; @@ -949,7 +945,7 @@ async IAsyncEnumerable AsyncLoop() iteration++; StreamEntry[] entries = await db.StreamReadAsync( - key, + uri, startPosition, READ_BY_ID_CHUNK_SIZE, CommandFlags.DemandMaster); diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 5db9304d..815d874e 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -41,7 +41,7 @@ async ValueTask IConsumerStorageStrategy.LoadBucketAsync( Func getProperty, CancellationToken cancellation) { - string key = $"{meta.Key()}:{type}:{meta.MessageId}"; + string key = $"{meta.FullUri()}:{type}:{meta.MessageId}"; IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 8f6353f4..dc1a6d23 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -119,7 +119,7 @@ async Task LocalStreamAddAsync() IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); using var scope = SuppressInstrumentationScope.Begin(); - var k = meta.Key(); + var k = meta.FullUri(); var result = await db.StreamAddAsync(k, entries, flags: CommandFlags.DemandMaster); return result; @@ -128,14 +128,14 @@ async Task LocalStreamAddAsync() catch (RedisConnectionException ex) { - _logger.LogError(ex, "REDIS Connection Failure: push event [{id}] into the [{partition}->{shard}] stream: {operation}", - meta.MessageId, meta.Partition, meta.Shard, meta.Operation); + _logger.LogError(ex, "REDIS Connection Failure: push event [{id}] into the [{URI}] stream: {operation}", + meta.MessageId, meta.Uri, meta.Operation); throw; } catch (Exception ex) { - _logger.LogError(ex, "Fail to push event [{id}] into the [{partition}->{shard}] stream: {operation}", - meta.MessageId, meta.Partition, meta.Shard, meta.Operation); + _logger.LogError(ex, "Fail to push event [{id}] into the [{URI}] stream: {operation}", + meta.MessageId, meta.Uri, meta.Operation); throw; } diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 7a131cb8..121ecde7 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -62,15 +62,15 @@ async ValueTask> IProducerStorageStrategy.S .Select(sgm => new HashEntry(sgm.Key, sgm.Value)) .ToArray(); - var key = $"{meta.Key()}:{type}:{id}"; + var key = $"{meta.FullUri()}:{type}:{id}"; await db.HashSetAsync(key, segmentsEntities); - return ImmutableDictionary.Empty; // .Add($"redis:{type}:key", key); + return ImmutableDictionary.Empty; } catch (Exception ex) { - _logger.LogError(ex, "Fail to Save event's [{id}] buckets [{type}], into the [{partition}->{shard}] stream: {operation}, IsConnecting: {connecting}", - id, type, meta.Partition, meta.Shard, meta.Operation, conn.IsConnecting); + _logger.LogError(ex, "Fail to Save event's [{id}] buckets [{type}], into the [{URI}] stream: {operation}, IsConnecting: {connecting}", + id, type, meta.Uri, meta.Operation, conn.IsConnecting); throw; } } diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs index fcc0fb7d..a9bb9771 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs +++ b/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs @@ -21,7 +21,7 @@ public static void InjectTelemetryTags(this Metadata meta, Activity? activity) activity?.SetTag("messaging.destination", meta.Operation); activity?.SetTag("messaging.message_id", meta.MessageId); - activity?.SetTag("messaging.redis.key", $"{meta.Partition}:{meta.Shard}"); + activity?.SetTag("messaging.redis.key", meta.Uri); meta.InjectMetaTelemetryTags(activity); } diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs index 7b93a7f7..d9d74c48 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs +++ b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs @@ -73,7 +73,7 @@ async ValueTask> IProducerStorageStrategy.S { var date = DateTime.UtcNow; int index = Interlocked.Increment(ref _index); - string basePath = $"{meta.Partition}/{meta.Shard}/{date:yyyy-MM-dd/HH:mm}/{meta.Operation}/{id}/{index}/{type}"; + string basePath = $"{meta.Uri}/{date:yyyy-MM-dd/HH:mm}/{meta.Operation}/{id}/{index}/{type}"; var tasks = bucket.Select(SaveAsync); var propKeyToS3Key = await Task.WhenAll(tasks); string json = JsonSerializer.Serialize(propKeyToS3Key, SerializerOptionsWithIndent); diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs index b3e5063f..d4c2b784 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs @@ -4,8 +4,8 @@ /// Event Source producer builder. /// public interface IConsumerEnvironmentBuilder : - IConsumerPartitionBuilder, - IConsumerEnvironmentOfBuilder> + IConsumerPartitionBuilder, + IConsumerEnvironmentOfBuilder> { } } diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs index ad1452d7..ff743540 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs @@ -7,19 +7,10 @@ public interface IConsumerPartitionBuilder { /// - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// The stream identifier (the URI combined with the environment separate one stream from another) /// /// The partition key. /// - T Partition(string partition); + T Uri(string partition); } } diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs deleted file mode 100644 index 881ced4c..00000000 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardBuilder.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace EventSource.Backbone.Building -{ - /// - /// Event Source producer builder. - /// - public interface IConsumerShardBuilder : - IConsumerReadyBuilder, IConsumerShardOfBuilder - { - } -} diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs deleted file mode 100644 index c243a5ed..00000000 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerShardOfBuilder.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace EventSource.Backbone.Building -{ - /// - /// Event Source producer builder. - /// - public interface IConsumerShardOfBuilder - { - - /// - /// Shard key represent physical sequence. - /// On the consumer side shard is optional - /// for listening on a physical source rather on the entire partition. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. - /// - /// The shard key. - /// - T Shard(string shardKey); - } -} diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs index add5ff9c..5f3b5493 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs @@ -22,11 +22,11 @@ public interface IConsumerPlan : IConsumerPlanBase IConsumerPlan ChangeEnvironment(Env? environment); /// - /// change the partition. + /// change the stream's key (identity). /// /// The partition. /// An IConsumerPlan. - IConsumerPlan ChangePartition(Env? partition); + IConsumerPlan ChangeKey(string? partition); /// /// Gets the storage strategies. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs index 7a06ac32..88e97e30 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs +++ b/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs @@ -56,13 +56,6 @@ public interface IConsumerPlanBase : IPlanRoute /// IImmutableList SegmentationStrategies { get; } - /// - /// Attach the shard. - /// - /// The shard. - /// - IConsumerPlan WithShard(string shard); - /// /// Gets or sets the invocation resilience policy. /// @@ -74,21 +67,21 @@ public interface IConsumerPlanBase : IPlanRoute /// public static class IConsumerPlanBaseExtensions { - #region Key + #region FullUri /// - /// Gets the partition:shard as key. + /// The stream's full identifier which is a combination of the URI and the environment /// - public static string Key(this IConsumerPlanBase meta, char separator = ':') + public static string FullUri(this IConsumerPlanBase meta, char separator = ':') { if (string.IsNullOrEmpty(meta.Environment)) - return $"{meta.Partition}{separator}{meta.Shard}"; + return meta.Uri; Env env = meta.Environment; string envFormatted = env.Format(); - return $"{envFormatted}{separator}{meta.Partition}{separator}{meta.Shard}"; + return $"{envFormatted}{separator}{meta.Uri}"; } - #endregion // Key + #endregion // FullUri } } \ No newline at end of file diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index 098a769e..22602c83 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -134,7 +134,7 @@ private async ValueTask ConsumingAsync( var consumerMeta = new ConsumerMetadata(meta, cancellation); var logger = Logger; - logger.LogDebug("Consuming event: {0}", meta.Key()); + logger.LogDebug("Consuming event: {0}", meta.FullUri()); #region _plan.Interceptors.InterceptAsync(...) @@ -179,14 +179,14 @@ private async ValueTask ConsumingAsync( } return false; }, cancellation); - logger.LogDebug("Consumed event: {0}", meta.Key()); + logger.LogDebug("Consumed event: {0}", meta.FullUri()); var options = Plan.Options; var behavior = options.AckBehavior; if (partialBehavior == PartialConsumerBehavior.ThrowIfNotHandled && !hasProcessed) { - Logger.LogCritical("No handler is matching event: {stream}, operation{operation}, MessageId:{id}", meta.Key(), meta.Operation, meta.MessageId); - throw new InvalidOperationException($"No handler is matching event: {meta.Key()}, operation{meta.Operation}, MessageId:{meta.MessageId}"); + Logger.LogCritical("No handler is matching event: {stream}, operation{operation}, MessageId:{id}", meta.FullUri(), meta.Operation, meta.MessageId); + throw new InvalidOperationException($"No handler is matching event: {meta.FullUri()}, operation{meta.Operation}, MessageId:{meta.MessageId}"); } if (hasProcessed) { @@ -210,7 +210,7 @@ private async ValueTask ConsumingAsync( catch (OperationCanceledException) { - logger.LogWarning("Canceled event: {0}", meta.Key()); + logger.LogWarning("Canceled event: {0}", meta.FullUri()); if (Plan.Options.AckBehavior != AckBehavior.OnFinally) { await ack.CancelAsync(); @@ -219,7 +219,7 @@ private async ValueTask ConsumingAsync( } catch (Exception ex) { - logger.LogError(ex, "event: {0}", meta.Key()); + logger.LogError(ex, "event: {0}", meta.FullUri()); if (Plan.Options.AckBehavior != AckBehavior.OnFinally) { await ack.CancelAsync(); diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs index 2d8236e1..45b2972c 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs @@ -59,9 +59,9 @@ IConsumerIterator IConsumerEnvironmentOfBuilder.Environment(E ///
/// The partition. /// - IConsumerIterator IConsumerPartitionBuilder.Partition(string partition) + IConsumerIterator IConsumerPartitionBuilder.Uri(string partition) { - IConsumerPlan plan = _plan.ChangePartition(partition); + IConsumerPlan plan = _plan.ChangeKey(partition); var result = new Iterator(plan); return result; } diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs index 00fc0302..823f755b 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs @@ -57,9 +57,9 @@ IConsumerReceiver IConsumerEnvironmentOfBuilder.Environment(E ///
/// The partition. /// - IConsumerReceiver IConsumerPartitionBuilder.Partition(string partition) + IConsumerReceiver IConsumerPartitionBuilder.Uri(string partition) { - IConsumerPlan plan = _plan.ChangePartition(partition); + IConsumerPlan plan = _plan.ChangeKey(partition); var result = new Receiver(plan); return result; } diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs index 84aa003e..599790d7 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -19,7 +19,7 @@ namespace EventSource.Backbone [DebuggerDisplay("{_plan.Environment}:{_plan.Partition}:{_plan.Shard}")] public partial class ConsumerBuilder : IConsumerBuilder, - IConsumerShardBuilder, + IConsumerReadyBuilder, IConsumerStoreStrategyBuilder { private readonly ConsumerPlan _plan = ConsumerPlan.Empty; @@ -149,7 +149,7 @@ IConsumerSubscribeBuilder IConsumerSubscribeBuilder.WithOptions(Func /// The environment (null: keep current environment, empty: reset the environment to nothing). /// - IConsumerPartitionBuilder IConsumerEnvironmentOfBuilder>.Environment(Env? environment) + IConsumerPartitionBuilder IConsumerEnvironmentOfBuilder>.Environment(Env? environment) { if (environment == null) return this; @@ -191,12 +191,13 @@ IConsumerSubscribeBuilder IConsumerEnvironmentOfBuilder - /// The partition key. + /// + /// The stream identifier (the URI combined with the environment separate one stream from another) + /// /// - IConsumerShardBuilder IConsumerPartitionBuilder.Partition( - string partition) + IConsumerReadyBuilder IConsumerPartitionBuilder.Uri(string uri) { - var prms = _plan.WithPartition(partition); + var prms = _plan.WithKey(uri); var result = new ConsumerBuilder(prms); return result; } @@ -213,60 +214,20 @@ IConsumerShardBuilder IConsumerPartitionBuilder.Partition /// central place without affecting sequence of specific order /// flow or limiting the throughput. ///
- /// The partition key. + /// + /// The stream identifier (the URI combined with the environment separate one stream from another) + /// /// - IConsumerSubscribeBuilder IConsumerPartitionBuilder.Partition( - string partition) + IConsumerSubscribeBuilder IConsumerPartitionBuilder.Uri( + string uri) { - var prms = _plan.WithPartition(partition); + var prms = _plan.WithKey(uri); var result = new ConsumerBuilder(prms); return result; } #endregion // Partition - #region Shard - - /// - /// Shard key represent physical sequence. - /// On the consumer side shard is optional - /// for listening on a physical source rather on the entire partition. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. - /// - /// The shard key. - /// - /// - IConsumerReadyBuilder IConsumerShardOfBuilder.Shard(string shard) - { - var prms = _plan.WithShard(shard); - var result = new ConsumerBuilder(prms); - return result; - } - - ///// - ///// Shard key represent physical sequence. - ///// On the consumer side shard is optional - ///// for listening on a physical source rather on the entire partition. - ///// Use same shard when order is matter. - ///// For example: assuming each ORDERING flow can have its - ///// own messaging sequence, in this case you can split each - ///// ORDER into different shard and gain performance bust.. - ///// - ///// The shard key. - ///// - ///// - //IConsumerSubscribeBuilder IConsumerShardOfBuilder.Shard(string shard) - //{ - // var prms = _plan.WithShard(shard); - // var result = new ConsumerBuilder(prms); - // return result; - //} - - #endregion // Shard - #region RegisterSegmentationStrategy /// @@ -604,11 +565,8 @@ private static JsonElement ToJson( w.WritePropertyName("__env__"); w.WriteStringValue(announcement.Environment); - w.WritePropertyName("__partition__"); - w.WriteStringValue(announcement.Partition); - - w.WritePropertyName("__shard__"); - w.WriteStringValue(announcement.Shard); + w.WritePropertyName("__uri__"); + w.WriteStringValue(announcement.Uri); w.WritePropertyName("__operation__"); w.WriteStringValue(announcement.Operation); @@ -634,9 +592,9 @@ private static JsonElement ToJson( } catch { } - var err = $"GetJsonByIdAsync [{entryId}, {announcement.Key()}]: failed to deserialize key='{key}', base64='{Convert.ToBase64String(val.ToArray())}', data={encoded}"; + var err = $"GetJsonByIdAsync [{entryId}, {announcement.FullUri()}]: failed to deserialize key='{key}', base64='{Convert.ToBase64String(val.ToArray())}', data={encoded}"; plan.Logger.LogError(ex.FormatLazy(), "GetJsonByIdAsync [{id}, {at}]: failed to deserialize key='{key}', base64='{value}', data={data}", - entryId, announcement.Key(), key, + entryId, announcement.FullUri(), key, Convert.ToBase64String(val.ToArray()), encoded); throw new DataMisalignedException(err, ex); @@ -701,9 +659,9 @@ private static JsonElement ToJson( } catch { } - var err = $"GetJsonByIdAsync [{entryId}, {announcement.Metadata.Key()}]: failed to deserialize key='{key}', base64='{Convert.ToBase64String(val.ToArray())}', data={encoded}"; + var err = $"GetJsonByIdAsync [{entryId}, {announcement.Metadata.FullUri()}]: failed to deserialize key='{key}', base64='{Convert.ToBase64String(val.ToArray())}', data={encoded}"; plan.Logger.LogError(ex.FormatLazy(), "GetJsonByIdAsync [{id}, {at}]: failed to deserialize key='{key}', base64='{value}', data={data}", - entryId, announcement.Metadata.Key(), key, + entryId, announcement.Metadata.FullUri(), key, Convert.ToBase64String(val.ToArray()), encoded); throw new DataMisalignedException(err, ex); diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs index a9954314..8a1cfbe2 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -43,8 +43,7 @@ private ConsumerPlan() /// The channel. /// The channel. /// The environment. - /// The partition. - /// The shard. + /// The stream's key (identity). /// The logger. /// The options. /// The segmentation strategies. @@ -62,8 +61,7 @@ private ConsumerPlan( Func? channelFactory = null, IConsumerChannelProvider? channel = null, string? environment = null, - string? partition = null, - string? shard = null, + string? key = null, ILogger? logger = null, ConsumerOptions? options = null, IImmutableList? segmentationStrategies = null, @@ -78,8 +76,7 @@ private ConsumerPlan( ChannelFactory = channelFactory ?? copyFrom.ChannelFactory; _channel = channel ?? copyFrom._channel; Environment = environment ?? copyFrom.Environment; - Partition = partition ?? copyFrom.Partition; - Shard = shard ?? copyFrom.Shard; + Uri = key ?? copyFrom.Uri; Logger = logger ?? copyFrom.Logger; Options = options ?? copyFrom.Options; SegmentationStrategies = segmentationStrategies ?? copyFrom.SegmentationStrategies; @@ -167,37 +164,15 @@ IConsumerChannelProvider IConsumerPlan.Channel #endregion // Environment - #region Partition + #region Key /// - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// The stream's key (identity) /// - public string Partition { get; } = string.Empty; + public string Uri { get; } = string.Empty; #endregion // Partition - #region Shard - - /// - /// Shard key represent physical sequence. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. - /// - public string Shard { get; } = string.Empty; - - #endregion // Shard - #region Options /// @@ -418,55 +393,35 @@ IConsumerPlan IConsumerPlan.ChangeEnvironment(Env? environment) #endregion // ChangeEnvironment - #region ChangePartition + #region ChangeKey /// - /// Attach the environment. + /// change the stream's key. /// /// The partition. /// - IConsumerPlan IConsumerPlan.ChangePartition(Env? partition) + IConsumerPlan IConsumerPlan.ChangeKey(string? partition) { if (partition == null) return this; - return new ConsumerPlan(this, partition: partition); + return WithKey(partition); } - #endregion // ChangePartition + #endregion // ChangeKey - #region WithPartition + #region WithKey /// - /// Attach the partition. + /// Attach a key. /// /// The partition. /// - internal ConsumerPlan WithPartition(string partition) - { - return new ConsumerPlan(this, partition: partition); - } - - #endregion // WithPartition - - #region WithShard - - /// - /// Attach the shard. - /// - /// The shard. - /// - IConsumerPlan IConsumerPlanBase.WithShard(string shard) => WithShard(shard); - /// - /// Attach the shard. - /// - /// The shard. - /// - internal ConsumerPlan WithShard(string shard) + internal ConsumerPlan WithKey(string partition) { - return new ConsumerPlan(this, shard: shard); + return new ConsumerPlan(this, key: partition); } - #endregion // WithShard + #endregion // WithKey #region WithResiliencePolicy diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs b/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs index 0cc80cfe..cd78ee80 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs @@ -40,9 +40,8 @@ public record AnnouncementData : Metadata EventKey = this.EventKey, MessageId = this.MessageId, Operation = this.Operation, - Partition = this.Partition, + Uri = this.Uri, ProducedAt = this.ProducedAt, - Shard = this.Shard, }; #endregion // ExtractMeta @@ -69,8 +68,7 @@ public static implicit operator AnnouncementData(Announcement announcement) MessageId = meta.MessageId, ProducedAt = meta.ProducedAt, Operation = meta.Operation, - Partition = meta.Partition, - Shard = meta.Shard + Uri = meta.Uri, }; } diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs b/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs index be7fb769..14063330 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs +++ b/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs @@ -50,7 +50,7 @@ public record Metadata /// /// Gets or sets the operation. /// - public string Operation { get; init; } = string.Empty; + public required string Operation { get; init; } #endregion Operation @@ -63,14 +63,14 @@ public record Metadata #endregion Environment - #region Partition + #region Uri /// - /// Gets or sets the partition. + /// The stream identifier (the URI combined with the environment separate one stream from another) /// - public string Partition { get; init; } = string.Empty; + public required string Uri { get; init; } - #endregion Partition + #endregion Uri #region Origin @@ -90,15 +90,6 @@ public record Metadata #endregion // Linked - #region Shard - - /// - /// Gets or sets the shard. - /// - public string Shard { get; init; } = string.Empty; - - #endregion Shard - #region ChannelType /// @@ -116,7 +107,7 @@ public record Metadata /// /// A that represents this instance. /// - public override string ToString() => $"{EventKey}, {this.Key()}"; + public override string ToString() => $"{EventKey}, {this.FullUri()}"; #endregion // ToString } @@ -128,7 +119,7 @@ public static class MetadataExtensions { private const string EMPTY_KEY = "~EMPTY~"; - public static readonly Metadata Empty = new Metadata { MessageId = EMPTY_KEY }; + public static readonly Metadata Empty = new Metadata { MessageId = EMPTY_KEY, ChannelType = "NONE", EventKey = string.Empty, Operation="NONE", Uri=string.Empty }; #region Duration @@ -139,21 +130,21 @@ public static class MetadataExtensions #endregion // Duration - #region Key + #region FullUri /// /// Gets the partition:shard as key. /// - public static string Key(this Metadata meta, char separator = ':') + public static string FullUri(this Metadata meta, char separator = ':') { if (string.IsNullOrEmpty(meta.Environment)) - return $"{meta.Partition}{separator}{meta.Shard}"; + return meta.Uri; Env env = meta.Environment; string envFormatted = env.Format(); - return $"{envFormatted}{separator}{meta.Partition}{separator}{meta.Shard}"; + return $"{envFormatted}{separator}{meta.Uri}"; } - #endregion // Key + #endregion // FullUri #region IsEmpty diff --git a/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs b/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs index b555d38f..0c86d830 100644 --- a/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs +++ b/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs @@ -8,34 +8,10 @@ public interface IPlanRoute /// /// Environment (part of the stream key). /// - /// - /// The partition. - /// Env Environment { get; } /// - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// The stream identifier (the URI combined with the environment separate one stream from another) /// - /// - /// The partition. - /// - string Partition { get; } - /// - /// Shard key represent physical sequence. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. - /// - string Shard { get; } - + string Uri { get; } } } \ No newline at end of file diff --git a/EventSource.Backbone.Contracts/TelemetryrExtensions.cs b/EventSource.Backbone.Contracts/TelemetryrExtensions.cs index bdcf94f6..4f9a96f4 100644 --- a/EventSource.Backbone.Contracts/TelemetryrExtensions.cs +++ b/EventSource.Backbone.Contracts/TelemetryrExtensions.cs @@ -25,8 +25,7 @@ public static TracerProviderBuilder ListenToEventSourceRedisChannel(this TracerP /// The activity. public static void InjectMetaTelemetryTags(this Metadata meta, Activity? activity) { - activity?.SetTag("event-source.partition", meta.Partition); - activity?.SetTag("event-source.shard", meta.Shard); + activity?.SetTag("event-source.uri", meta.Uri); activity?.SetTag("event-source.operation", meta.Operation); activity?.SetTag("event-source.message-id", meta.MessageId); activity?.SetTag("event-source.channel-type", meta.ChannelType); diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs index d8400b0a..747e7f47 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs @@ -7,19 +7,12 @@ public interface IProducerPartitionBuilder : IProducerRawBuilder, IProducerLoggerBuilder { /// - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// The stream key /// - /// The partition key. + /// + /// The stream identifier (the URI combined with the environment separate one stream from another) + /// /// - IProducerShardBuilder Partition(string partition); + IProducerHooksBuilder Uri(string uri); } } diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs deleted file mode 100644 index 59f9a2bf..00000000 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerShardBuilder.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace EventSource.Backbone.Building -{ - /// - /// Enable configuration. - /// - public interface IProducerShardBuilder : IProducerRawBuilder, IProducerLoggerBuilder - { - /// - /// Shard key represent physical sequence. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. - /// - /// The shard key. - /// - IProducerHooksBuilder Shard(string shard); - } -} diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs index c08eac56..34da9544 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs @@ -12,10 +12,10 @@ public interface IProducerOverrideBuilder : IProducerOverrideEnvironmentBuild /// /// Dynamic override of the stream id before sending. - /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. + /// Can use for scenario like routing between environment like dev vs. prod or AWS vs Azure. /// /// The routing strategy. /// - IProducerOverrideBuildBuilder Strategy(Func routeStrategy); + IProducerOverrideBuildBuilder Strategy(Func routeStrategy); } } diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs index c87d0e56..00e7c167 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs @@ -4,7 +4,7 @@ /// Enable dynamic transformation of the stream id before sending. /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. /// - public interface IProducerOverridePartitionBuilder : IProducerOverrideShardBuilder where T : class + public interface IProducerOverridePartitionBuilder : IProducerOverrideBuildBuilder where T : class { /// @@ -14,6 +14,6 @@ public interface IProducerOverridePartitionBuilder : IProducerOverrideShardBu /// The partition. /// The type. /// - IProducerOverrideShardBuilder Partition(string partition, RouteAssignmentType type = RouteAssignmentType.Prefix); + IProducerOverrideBuildBuilder Partition(string partition, RouteAssignmentType type = RouteAssignmentType.Prefix); } } diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs b/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs deleted file mode 100644 index 829080b4..00000000 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideShardBuilder.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace EventSource.Backbone.Building -{ - /// - /// Enable dynamic transformation of the stream id before sending. - /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - /// - public interface IProducerOverrideShardBuilder : IProducerOverrideBuildBuilder where T : class - { - /// - /// Override the shard. - /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - /// - /// The shard. - /// The type. - /// - IProducerOverrideBuildBuilder Shard(string shard, RouteAssignmentType type = RouteAssignmentType.Prefix); - } -} diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs b/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs index f4d49c50..be43718d 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs @@ -33,7 +33,7 @@ private ProducerPlan() /// The channel. /// The channel. /// The environment. - /// The partition. + /// The partition. /// The shard. /// The logger. /// The options. @@ -48,8 +48,7 @@ private ProducerPlan( Func? channelFactory = null, IProducerChannelProvider? channel = null, string? environment = null, - string? partition = null, - string? shard = null, + string? key = null, ILogger? logger = null, EventSourceOptions? options = null, IImmutableList? segmentationStrategies = null, @@ -62,8 +61,7 @@ private ProducerPlan( ChannelFactory = channelFactory ?? copyFrom.ChannelFactory; _channel = channel ?? copyFrom._channel; Environment = environment ?? copyFrom.Environment; - Partition = partition ?? copyFrom.Partition; - Shard = shard ?? copyFrom.Shard; + Uri = key ?? copyFrom.Uri; Options = options ?? copyFrom.Options; SegmentationStrategies = segmentationStrategies ?? copyFrom.SegmentationStrategies; Interceptors = interceptors ?? copyFrom.Interceptors; @@ -145,36 +143,14 @@ IProducerChannelProvider IProducerPlan.Channel #endregion // Environment - #region Partition - - /// - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. - /// - public string Partition { get; } = string.Empty; - - #endregion // Partition - - #region Shard + #region Key /// - /// Shard key represent physical sequence. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. + /// The stream key /// - public string Shard { get; } = string.Empty; + public string Uri { get; } = string.Empty; - #endregion // Shard + #endregion // Key #region Options @@ -325,59 +301,37 @@ public ProducerPlan WithEnvironment( { return type switch { - RouteAssignmentType.Prefix => new ProducerPlan(this, environment: environment, partition: $"{partition}{this.Partition}", shard: $"{shard}{this.Shard}"), - RouteAssignmentType.Replace => new ProducerPlan(this, environment: environment, partition: partition ?? this.Partition, shard: shard ?? this.Shard), - RouteAssignmentType.Suffix => new ProducerPlan(this, environment: environment, partition: $"{this.Partition}{partition}", shard: $"{this.Shard}{shard}"), + RouteAssignmentType.Prefix => new ProducerPlan(this, environment: environment, key: $"{partition}{this.Uri}"), + RouteAssignmentType.Replace => new ProducerPlan(this, environment: environment, key: partition ?? this.Uri), + RouteAssignmentType.Suffix => new ProducerPlan(this, environment: environment, key: $"{this.Uri}{partition}"), _ => this, }; } #endregion // WithEnvironment - #region WithPartition + #region WithKey /// - /// Withes the partition. + /// Withes the stream's key (identifier). /// /// The partition. /// The shard. /// The type. /// - public ProducerPlan WithPartition(string partition, string? shard = null, - RouteAssignmentType type = RouteAssignmentType.Replace) - { - return type switch - { - RouteAssignmentType.Prefix => new ProducerPlan(this, partition: $"{partition}{this.Partition}", shard: $"{shard}{this.Shard}"), - RouteAssignmentType.Replace => new ProducerPlan(this, partition: partition, shard: shard ?? this.Shard), - RouteAssignmentType.Suffix => new ProducerPlan(this, partition: $"{this.Partition}{partition}", shard: $"{this.Shard}{shard}"), - _ => this, - }; - } - - #endregion // WithPartition - - #region WithShard - - /// - /// Withes the shard. - /// - /// The shard. - /// The type. - /// - public ProducerPlan WithShard(string shard, + public ProducerPlan WithKey(string partition, string? shard = null, RouteAssignmentType type = RouteAssignmentType.Replace) { return type switch { - RouteAssignmentType.Prefix => new ProducerPlan(this, shard: $"{shard}{this.Shard}"), - RouteAssignmentType.Replace => new ProducerPlan(this, shard: shard ?? this.Shard), - RouteAssignmentType.Suffix => new ProducerPlan(this, shard: $"{this.Shard}{shard}"), + RouteAssignmentType.Prefix => new ProducerPlan(this, key: $"{partition}{this.Uri}"), + RouteAssignmentType.Replace => new ProducerPlan(this, key: partition), + RouteAssignmentType.Suffix => new ProducerPlan(this, key: $"{this.Uri}{partition}"), _ => this, }; } - #endregion // WithShard + #endregion // WithKey #region AddRoute diff --git a/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs b/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs index 7030e4d4..0c321ce5 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs +++ b/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs @@ -228,8 +228,7 @@ private async ValueTask SendAsync( { MessageId = id, Environment = plan.Environment, - Partition = plan.Partition, - Shard = plan.Shard, + Uri = plan.Uri, Operation = operation }; diff --git a/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs index aef76f65..3428bf13 100644 --- a/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs @@ -11,7 +11,6 @@ namespace EventSource.Backbone /// public class ProducerBuilder : IProducerBuilder, - IProducerShardBuilder, IProducerHooksBuilder, IProducerStoreStrategyBuilder { @@ -163,49 +162,20 @@ IProducerSpecializeBuilder IProducerBuilderEnvironment - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// The stream's key /// - /// The partition key. + /// The partition key. /// - IProducerShardBuilder IProducerPartitionBuilder.Partition(string partition) + IProducerHooksBuilder IProducerPartitionBuilder.Uri(string key) { - var prms = Plan.WithPartition(partition); + var prms = Plan.WithKey(key); return new ProducerBuilder(prms); } - #endregion // Partition - - #region Shard - - /// - /// Shard key represent physical sequence. - /// Use same shard when order is matter. - /// For example: assuming each ORDERING flow can have its - /// own messaging sequence, in this case you can split each - /// ORDER into different shard and gain performance bust.. - /// - /// The shard key. - /// - /// - IProducerHooksBuilder IProducerShardBuilder.Shard(string shard) - { - var prms = Plan.WithShard(shard); - return new ProducerBuilder(prms); - } - - #endregion // Shard + #endregion // Key #region WithOptions @@ -317,16 +287,6 @@ private ProducerBuilder WithLogger(ILogger logger) return new ProducerBuilder(prms); } - /// - /// Attach logger. - /// - /// The logger. - /// - IProducerShardBuilder IProducerLoggerBuilder.WithLogger(ILogger logger) - { - return WithLogger(logger); - } - /// /// Attach logger. /// @@ -482,14 +442,14 @@ T IProducerOverrideBuildBuilder.Build(Func factory) /// /// Dynamic override of the stream id before sending. - /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. + /// Can use for scenario like routing between environment like dev vs. prod or AWS vs azure. /// /// The routing strategy. /// - IProducerOverrideBuildBuilder IProducerOverrideBuilder.Strategy(Func routeStrategy) + IProducerOverrideBuildBuilder IProducerOverrideBuilder.Strategy(Func routeStrategy) { - var (environment, partition, shard) = routeStrategy(_plan); - var plan = _plan.WithEnvironment(environment ?? _plan.Environment, partition, shard); + var (environment, uri) = routeStrategy(_plan); + var plan = _plan.WithEnvironment(environment ?? _plan.Environment, uri); return new Router(plan); } @@ -520,30 +480,13 @@ IProducerOverridePartitionBuilder IProducerOverrideEnvironmentBuilder.Envi /// The partition. /// The type. /// - IProducerOverrideShardBuilder IProducerOverridePartitionBuilder.Partition(string partition, RouteAssignmentType type) + IProducerOverrideBuildBuilder IProducerOverridePartitionBuilder.Partition(string partition, RouteAssignmentType type) { - var plan = _plan.WithPartition(partition, type: type); + var plan = _plan.WithKey(partition, type: type); return new Router(plan); } #endregion // Partition - - #region Shard - - /// - /// Override the shard. - /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. - /// - /// The shard. - /// The type. - /// - IProducerOverrideBuildBuilder IProducerOverrideShardBuilder.Shard(string shard, RouteAssignmentType type) - { - var plan = _plan.WithShard(shard, type); - return new Router(plan); - } - - #endregion // Shard } #endregion // Router @@ -591,8 +534,7 @@ public async ValueTask Produce(Announcement data) { MessageId = Guid.NewGuid().ToString("N"), Environment = IsNullOrEmpty(_plan.Environment) ? metadata.Environment : _plan.Environment, - Partition = IsNullOrEmpty(_plan.Partition) ? metadata.Partition : _plan.Partition, - Shard = IsNullOrEmpty(_plan.Shard) ? metadata.Shard : _plan.Shard, + Uri = IsNullOrEmpty(_plan.Uri) ? metadata.Uri : _plan.Uri, Origin = MessageOrigin.Copy, Linked = metadata, }; diff --git a/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 9471d0c0..8802498d 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -31,8 +31,7 @@ public class EndToEndExplicitTests : IDisposable private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerStoreStrategyBuilder _consumerBuilder; - private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly string SHARD = $"some-shard-{DateTime.UtcNow.Second}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); @@ -98,8 +97,7 @@ public async Task OnSucceed_ACK_Test() IEventFlowProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) //.WithLogger(_fakeLogger) .BuildEventFlowProducer(); @@ -120,8 +118,7 @@ public async Task OnSucceed_ACK_Test() .WithOptions(o => consumerOptions) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger); await using IConsumerLifetime subscription = builder .Group("CONSUMER_GROUP_1") @@ -185,7 +182,7 @@ private static CancellationToken GetCancellationToken() public void Dispose() { GC.SuppressFinalize(this); - string key = $"{PARTITION}:{SHARD}"; + string key = URI; IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( _fakeLogger, cfg => cfg.AllowAdmin = true); diff --git a/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs index 9241576f..9640bf7b 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -27,8 +27,7 @@ public class EndToEndStressTests : IDisposable private readonly IConsumerHooksBuilder _consumerBuilder; private readonly string ENV = $"test"; - private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly string SHARD = $"some-shard-{DateTime.UtcNow.Second}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; @@ -97,8 +96,7 @@ public async Task Receiver_Stress_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -113,8 +111,7 @@ public async Task Receiver_Stress_Test() IConsumerReceiver receiver = _consumerBuilder .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildReceiver(); @@ -138,8 +135,7 @@ public async Task Receiver_Json_Stress_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -152,8 +148,7 @@ public async Task Receiver_Json_Stress_Test() IConsumerReceiver receiver = _consumerBuilder .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildReceiver(); @@ -253,7 +248,7 @@ public void Dispose() cfg => cfg.AllowAdmin = true); string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); IDatabaseAsync db = conn.GetDatabase(); var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); diff --git a/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs index b700ce5f..004c8456 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs @@ -44,8 +44,7 @@ public class EndToEndTests : IDisposable private readonly IEventFlowStage2Consumer _stage2Consumer = A.Fake(); private readonly string ENV = $"Development"; - private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly string SHARD = $"some-shard-{DateTime.UtcNow.Second}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; @@ -144,8 +143,7 @@ public async Task Environmet_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -161,8 +159,7 @@ public async Task Environmet_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -196,8 +193,7 @@ public async Task PartialConsumer_Strict_Succeed_Test() IEventFlowProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildEventFlowProducer(); @@ -215,8 +211,7 @@ public async Task PartialConsumer_Strict_Succeed_Test() .WithOptions(o => DefaultOptions(o, 2, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_X_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -252,8 +247,7 @@ public async Task PartialConsumer_Strict_Fail_Test() IEventFlowProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildEventFlowProducer(); @@ -271,8 +265,7 @@ public async Task PartialConsumer_Strict_Fail_Test() .WithOptions(o => DefaultOptions(o, 2, AckBehavior.OnSucceed) with { PartialBehavior = PartialConsumerBehavior.ThrowIfNotHandled }) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_X_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -299,8 +292,7 @@ public async Task PartialConsumer_Allow_Test() IEventFlowProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildEventFlowProducer(); @@ -324,8 +316,7 @@ public async Task PartialConsumer_Allow_Test() .WithOptions(o => DefaultOptions(o, times * 2, AckBehavior.OnSucceed, PartialConsumerBehavior.Loose)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_X_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -355,8 +346,7 @@ public async Task Receiver_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -372,8 +362,7 @@ public async Task Receiver_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildReceiver(); @@ -403,8 +392,7 @@ public async Task Receiver_Json_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -420,8 +408,7 @@ public async Task Receiver_Json_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildReceiver(); @@ -458,8 +445,7 @@ public async Task Receiver_ChangeEnvironment_AfterBuild() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment("FakeTest") - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Environment(ENV) .BuildSequenceOperationsProducer(); @@ -476,8 +462,7 @@ public async Task Receiver_ChangeEnvironment_AfterBuild() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment("DemoTest") - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildReceiver() .Environment(ENV); @@ -505,8 +490,7 @@ public async Task Receiver_ChangeEnvironment_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment("FakeTest") - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Environment(ENV) .BuildSequenceOperationsProducer(); @@ -523,8 +507,7 @@ public async Task Receiver_ChangeEnvironment_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment("DemoTest") - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Environment(ENV) .BuildReceiver(); @@ -552,8 +535,7 @@ public async Task Iterator_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -569,8 +551,7 @@ public async Task Iterator_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator(); @@ -611,8 +592,7 @@ public async Task Iterator_Cancellation_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -631,8 +611,7 @@ public async Task Iterator_Cancellation_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator(); @@ -684,8 +663,7 @@ public async Task Iterator_Json_NoMeta_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -701,8 +679,7 @@ public async Task Iterator_Json_NoMeta_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator(); @@ -750,8 +727,7 @@ public async Task Iterator_Json_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -767,8 +743,7 @@ public async Task Iterator_Json_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator(); @@ -812,11 +787,7 @@ public async Task Iterator_WithFilter_Json_Test() { #region ISequenceOperations producer = ... - ISequenceOperationsProducer producer = _producerBuilder - //.WithOptions(producerOption) - .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + ISequenceOperationsProducer producer = _producerBuilder.Environment(ENV).Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -832,8 +803,7 @@ public async Task Iterator_WithFilter_Json_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator(); @@ -876,8 +846,7 @@ public async Task Iterator_MapByType_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -893,8 +862,7 @@ public async Task Iterator_MapByType_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator() .SpecializeSequenceOperationsConsumer(); @@ -924,8 +892,7 @@ public async Task Iterator_MapByType_WithExtension_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -941,8 +908,7 @@ public async Task Iterator_MapByType_WithExtension_Test() .WithOptions(o => DefaultOptions(o)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator() .Specialize(UnitTests.Entities.SequenceOperationsConsumerEntityMapper.Default); @@ -972,8 +938,7 @@ public async Task OnSucceed_ACK_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -989,8 +954,7 @@ public async Task OnSucceed_ACK_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1024,8 +988,7 @@ public async Task Until_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -1046,8 +1009,7 @@ public async Task Until_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed) with { FetchUntilDateOrEmpty = DateTimeOffset.UtcNow }) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1082,8 +1044,7 @@ public async Task GeneratedContract_Test() ISequenceOperationsProducer producer1 = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -1094,8 +1055,7 @@ public async Task GeneratedContract_Test() IProducerSequenceOperations producer2 = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Build(ProducerSequenceOperationsBridgePipeline.Create); @@ -1112,8 +1072,7 @@ public async Task GeneratedContract_Test() .WithOptions(o => DefaultOptions(o, 6, AckBehavior.OnSucceed)) /* detach consumer after 6 messages*/ .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1148,8 +1107,7 @@ public async Task GeneratedContract_Factory_Test() ISequenceOperationsProducer producer1 = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -1160,8 +1118,7 @@ public async Task GeneratedContract_Factory_Test() IProducerSequenceOperations producer2 = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Build(ProducerSequenceOperationsBridgePipeline.Create); @@ -1178,8 +1135,7 @@ public async Task GeneratedContract_Factory_Test() .WithOptions(o => DefaultOptions(o, 6, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1213,8 +1169,7 @@ public async Task OnSucceed_ACK_WithFailure_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -1245,8 +1200,7 @@ public async Task OnSucceed_ACK_WithFailure_Test() .WithOptions(o => DefaultOptions(o, 4, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") @@ -1282,8 +1236,7 @@ public async Task OnFinaly_ACK_WithFailure_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -1315,8 +1268,7 @@ public async Task OnFinaly_ACK_WithFailure_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnFinally)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") @@ -1353,8 +1305,7 @@ public async Task Manual_ACK_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .BuildSequenceOperationsProducer(); #endregion // ISequenceOperations producer = ... @@ -1390,8 +1341,7 @@ public async Task Manual_ACK_Test() .WithOptions(o => DefaultOptions(o, 4, AckBehavior.Manual)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") @@ -1428,8 +1378,7 @@ public async Task Resilience_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .BuildSequenceOperationsProducer(); #endregion // ISequenceOperations producer = ... @@ -1459,8 +1408,7 @@ public async Task Resilience_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.Manual)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithResiliencePolicy(Policy.Handle().RetryAsync(3)) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") @@ -1495,15 +1443,13 @@ public async Task Override_Test() var producerBuilder = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger); ISequenceOperationsProducer producer = producerBuilder.BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerPrefix = producerBuilder .Specialize() .Environment("dev") .Partition("p0.") - .Shard("p1.") .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerPrefix1 = producerBuilder .Specialize() @@ -1511,7 +1457,6 @@ public async Task Override_Test() ISequenceOperationsProducer producerSuffix = producerBuilder .Specialize() .Partition(".s0", RouteAssignmentType.Suffix) - .Shard(".s1", RouteAssignmentType.Suffix) .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerSuffix1 = producerBuilder .Specialize() @@ -1519,7 +1464,7 @@ public async Task Override_Test() .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerDynamic = producerBuilder.Environment("Fake Env") .Specialize() - .Strategy(m => (ENV, $"d.{m.Partition}", $"{m.Shard}.d")) + .Strategy(m => (ENV, $"d.{m.Uri}")) .BuildSequenceOperationsProducer(); #endregion // ISequenceOperations producer = ... @@ -1539,8 +1484,7 @@ public async Task Override_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1550,8 +1494,7 @@ public async Task Override_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment("dev") - .Partition($"p0.{PARTITION}") - .Shard($"p1.{SHARD}") + .Uri($"p0.{URI}") .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1561,8 +1504,7 @@ public async Task Override_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition($"p2.{PARTITION}") - .Shard(SHARD) + .Uri($"p2.{URI}") .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1572,8 +1514,7 @@ public async Task Override_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition($"{PARTITION}.s0") - .Shard($"{SHARD}.s1") + .Uri($"{URI}.s0") .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1583,8 +1524,7 @@ public async Task Override_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition($"{PARTITION}.s2") - .Shard(SHARD) + .Uri($"{URI}.s2") .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") @@ -1594,8 +1534,7 @@ public async Task Override_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition($"d.{PARTITION}") - .Shard($"{SHARD}.d") + .Uri($"d.{URI}") .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_D") .Name($"TEST_D {DateTime.UtcNow:HH:mm:ss}") @@ -1673,8 +1612,7 @@ public async Task Claim_Test() ISequenceOperationsProducer producer = _producerBuilder .Environment(ENV) //.WithOptions(producerOption) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .BuildSequenceOperationsProducer(); #endregion // ISequenceOperations producer = ... @@ -1704,8 +1642,7 @@ public async Task Claim_Test() .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(URI) .WithResiliencePolicy(Policy.Handle().RetryAsync(3)) .WithLogger(_fakeLogger); @@ -1823,7 +1760,7 @@ public void Dispose() cfg => cfg.AllowAdmin = true); string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); IDatabaseAsync db = conn.GetDatabase(); var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); diff --git a/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs index 3be2986a..ab25dcd3 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs @@ -36,8 +36,7 @@ public class InheritanceTests : IDisposable private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"Development"; - private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly string SHARD = $"some-shard-{DateTime.UtcNow.Second}"; + private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1_000 * 50; @@ -104,8 +103,7 @@ public async Task Inheritance_PartialConsumer_Strict_Succeed_Test(MultiConsumerB IFlowABProducer producer = _producerBuilder .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .BuildFlowABProducer(); #endregion // ISequenceOperations producer = ... @@ -142,8 +140,7 @@ public async Task Inheritance_PartialConsumer_Strict_Succeed_Test(MultiConsumerB }) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .Group("CONSUMER_GROUP_X_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") .SubscribeFlowAConsumer(_subscriberA) @@ -191,8 +188,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() IFlowABProducer producer = _producerBuilder .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .BuildFlowABProducer(); #endregion // ISequenceOperations producer = ... @@ -255,8 +251,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() }) .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .Group("CONSUMER_GROUP_X_1"); await using IConsumerLifetime subscriptionA = diff --git a/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs index 0cb39cdb..599d2679 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -63,8 +63,7 @@ public async Task Migration_By_Receiver_Test() IRawProducer rawProducer = _targetProducerBuilder .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .WithLogger(_fakeLogger) .BuildRaw(new RawProducerOptions { KeepOriginalMeta = true }); @@ -76,8 +75,7 @@ public async Task Migration_By_Receiver_Test() IAsyncEnumerable announcements = _sourceConsumerBuilder .WithCancellation(cancellation) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .WithLogger(_fakeLogger) .BuildIterator() .GetAsyncEnumerable(new ConsumerAsyncEnumerableOptions { ExitWhenEmpty = true }); diff --git a/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs index 6e6eadba..a6c24f13 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs @@ -127,8 +127,7 @@ public async Task Migration_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -147,8 +146,7 @@ public async Task Migration_Test() var consumerBuilder = _consumerBuilder .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) - .Partition(PARTITION) - .Shard(SHARD) + .Uri(PARTITION) .WithLogger(_fakeLogger); #endregion // var consumerBuilder = _consumerBuilder... diff --git a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs b/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs index 8d86c29b..190a3eda 100644 --- a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs @@ -37,7 +37,7 @@ public void Build_Raw_Consumer_Direct_Test() .RegisterInterceptor(_rawAsyncInterceptor) .RegisterInterceptor(_rawInterceptor) .RegisterSegmentationStrategy(_segmentation) - .Partition("ORDERS") + .Uri("ORDERS") // .Shard("ORDER-AHS7821X") .WithLogger(_logger) .SubscribeSequenceOperationsConsumer(_subscriber); diff --git a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs b/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs index 797d1791..791e097b 100644 --- a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs @@ -41,20 +41,17 @@ public async Task Build_API_Merge_Producer_Test() { var producerA = _builder.UseChannel(_channel) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .AddInterceptor(_rawAsyncInterceptor); var producerB = _builder.UseTestProducerChannel1() - .Partition("NGOs") - .Shard("NGO #2782228") + .Uri("NGOs:NGO #2782228") .UseSegmentation(_segmentationStrategy); var producerC = _builder.UseChannel(_channel) - .Partition("Fans") - .Shard("Geek: @someone") + .Uri("Fans:Geek: @someone") .UseSegmentation(_otherSegmentationStrategy); @@ -82,8 +79,7 @@ public async Task Build_API_Serializer_Producer_Test() ISequenceOperationsProducer producer = _builder.UseChannel(_channel) .WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .WithLogger(_logger) .BuildSequenceOperationsProducer(); @@ -101,8 +97,7 @@ public async Task Build_API_Interceptor_Producer_Test() { ISequenceOperationsProducer producer = _builder.UseChannel(_channel) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .AddInterceptor(_rawInterceptor) .AddInterceptor(_rawAsyncInterceptor) .UseSegmentation(_segmentationStrategy) diff --git a/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs b/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs index 8ff3c407..1d0ac1b9 100644 --- a/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs @@ -39,7 +39,7 @@ public void Build_Raw_Consumer_Direct_Test() .RegisterInterceptor(_rawAsyncInterceptor) .RegisterInterceptor(_rawInterceptor) .RegisterSegmentationStrategy(_segmentation) - .Partition("ORDERS") + .Uri("ORDERS") // .Shard("ORDER-AHS7821X") .SubscribeSequenceOperationsConsumer(_subscriber); diff --git a/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs b/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs index 088794cd..3c9e93c4 100644 --- a/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs @@ -63,8 +63,7 @@ public async Task End2End_CustomBaseSubscription_Test() ISimpleEventProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .BuildSimpleEventProducer(); await producer.ExecuteAsync("Id", 1); @@ -78,8 +77,7 @@ public async Task End2End_CustomBaseSubscription_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions) .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .SubscribeSimpleEvent(_simpleEventConsumer); ch.Writer.Complete(); @@ -106,8 +104,7 @@ public async Task End2End_CustomSubscriptionBridge_Test() ISimpleEventProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .BuildSimpleEventProducer(); await producer.ExecuteAsync("Id", 1); @@ -121,8 +118,7 @@ public async Task End2End_CustomSubscriptionBridge_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions) .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .Subscribe(_simpleBridgeSubscription.BridgeAsync); ch.Writer.Complete(); @@ -149,8 +145,7 @@ public async Task End2End_GenBaseSubscription_Test() ISimpleEventProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .BuildSimpleEventProducer(); await producer.ExecuteAsync("Id", 1); @@ -164,8 +159,7 @@ public async Task End2End_GenBaseSubscription_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions) .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .Subscribe(_simpleGenSubscription); ch.Writer.Complete(); @@ -192,8 +186,7 @@ public async Task End2End_GenSubscriptionBridge_Test() ISimpleEventProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .BuildSimpleEventProducer(); await producer.ExecuteAsync("Id", 1); @@ -207,8 +200,7 @@ public async Task End2End_GenSubscriptionBridge_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions) .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .Subscribe(_simpleGenBridgeSubscription.BridgeAsync); ch.Writer.Complete(); @@ -235,8 +227,7 @@ public async Task End2End_Test() ISequenceOperationsProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .BuildSequenceOperationsProducer(); await producer.RegisterAsync(new User()); @@ -250,8 +241,7 @@ public async Task End2End_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions) .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids#HappySocks") .Subscribe(new SequenceOfConsumerBridge(_subscriber)); ch.Writer.Complete(); diff --git a/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs b/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs index b4884e41..b636bb50 100644 --- a/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs @@ -50,20 +50,17 @@ public async Task Build_Merge_Producer_Test() { var producerA = _builder.UseChannel(_channel) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .AddInterceptor(_rawAsyncInterceptor); var producerB = _builder.UseTestProducerChannel1() - .Partition("NGOs") - .Shard("NGO #2782228") + .Uri("NGOs:NGO #2782228") .UseSegmentation(_segmentationStrategy); var producerC = _builder.UseTestProducerChannel2() - .Partition("Fans") - .Shard("Geek: @someone") + .Uri("Fans:Geek: @someone") .UseSegmentation(_otherSegmentationStrategy); @@ -94,8 +91,7 @@ public async Task Build_Serializer_Producer_Test() ISequenceOperationsProducer producer = _builder.UseChannel(_channel) //.WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .BuildSequenceOperationsProducer(); await producer.RegisterAsync(new User()); @@ -117,8 +113,7 @@ public async Task Build_GeneratedFactory_Producer_Test() ISequenceOfProducer producer = _builder.UseChannel(_channel) //.WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .BuildSequenceOfProducer(); await producer.RegisterAsync(new User()); @@ -140,8 +135,7 @@ public async Task Build_GeneratedFactory_Specialize_Producer_Test() ISequenceOfProducer producer = _builder.UseChannel(_channel) //.WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .Specialize() .Environment("QA") .BuildSequenceOfProducer(); @@ -165,8 +159,7 @@ public async Task Build_Factory_Producer_Test() ISequenceOperationsProducer producer = _builder.UseChannel(_channel) //.WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .BuildSequenceOperationsProducer(); await producer.RegisterAsync(new User()); @@ -188,8 +181,7 @@ public async Task Build_Factory_Producer_WithReturn_Test() ISequenceOfProducer producer = _builder.UseChannel(_channel) //.WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .CustomBuildSequenceOfProducer(); string id1 = await producer.RegisterAsync(new User()); @@ -214,8 +206,7 @@ public async Task Build_Factory_Specialize_Producer_Test() ISequenceOperationsProducer producer = _builder.UseChannel(_channel) //.WithOptions(option) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .Specialize() .Environment("QA") .BuildSequenceOperationsProducer(); @@ -236,8 +227,7 @@ public async Task Build_Interceptor_Producer_Test() { ISequenceOperationsProducer producer = _builder.UseChannel(_channel) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .AddInterceptor(_rawInterceptor) .AddInterceptor(_rawAsyncInterceptor) .UseSegmentation(_segmentationStrategy) diff --git a/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs b/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs index 40f1d610..c15b999a 100644 --- a/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs @@ -13,8 +13,7 @@ public void Metadata_Serialization_Test() { MessageId = "message1", Environment = "env1", - Partition = "partition1", - Shard = "shard1", + Uri = "partition1", Operation = "operation1" }; @@ -56,8 +55,7 @@ public void Announcement_Serialization_Test() { MessageId = "message1", Environment = "env1", - Partition = "partition1", - Shard = "shard1", + Uri = "partition1", Operation = "operation1" }; meta = meta with { Linked = meta, Origin = MessageOrigin.Copy }; diff --git a/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs b/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs index 60748f42..51f63686 100644 --- a/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs @@ -57,8 +57,7 @@ public async Task Migration_Simple_Test() ISimpleEventProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .BuildSimpleEventProducer(); await producer.ExecuteAsync("Id", 1); @@ -78,8 +77,7 @@ public async Task Migration_Simple_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions)r .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .Subscribe(subscriptionBridge); // ASSERT @@ -87,10 +85,8 @@ public async Task Migration_Simple_Test() Assert.Equal(MessageOrigin.Copy, a1.Metadata.Origin); Assert.Equal(MessageOrigin.Original, a1.Metadata.Linked.Origin); Assert.Equal("ExecuteAsync", a1.Metadata.Operation); - Assert.Equal("Organizations", a1.Metadata.Partition); - Assert.Equal("Organizations", a1.Metadata.Linked.Partition); - Assert.Equal("Org: #RedSocks", a1.Metadata.Shard); - Assert.Equal("Org: #RedSocks", a1.Metadata.Linked.Shard); + Assert.Equal("Kids:HappySocks", a1.Metadata.Uri); + Assert.Equal("Kids:HappySocks", a1.Metadata.Linked.Uri); Assert.Equal(2, a1.Segments.Count()); Assert.True(a1.Segments.TryGet("key", out string k1)); @@ -131,8 +127,7 @@ public async Task Migration_Change_Target_Test() ISimpleEventProducer producer = _producerBuilder.UseChannel(_producerChannel) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .BuildSimpleEventProducer(); await producer.ExecuteAsync("Id", 1); @@ -143,8 +138,7 @@ public async Task Migration_Change_Target_Test() IRawProducer rawProducer = _producerBuilder.UseChannel(_rawProducerChannel) //.WithOptions(producerOption) - .Partition("New-Organizations") - .Shard("Org: #WhiteSocks") + .Uri("Man:Socks") .BuildRaw(); ISubscriptionBridge subscriptionBridge = new SubscriptionBridge(rawProducer); @@ -155,8 +149,7 @@ public async Task Migration_Change_Target_Test() _consumerBuilder.UseChannel(_consumerChannel) //.WithOptions(consumerOptions)r .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .Subscribe(subscriptionBridge); // ASSERT @@ -164,10 +157,8 @@ public async Task Migration_Change_Target_Test() Assert.Equal(MessageOrigin.Copy, a1.Metadata.Origin); Assert.Equal(MessageOrigin.Original, a1.Metadata.Linked.Origin); Assert.Equal("ExecuteAsync", a1.Metadata.Operation); - Assert.Equal("New-Organizations", a1.Metadata.Partition); - Assert.Equal("Organizations", a1.Metadata.Linked.Partition); - Assert.Equal("Org: #WhiteSocks", a1.Metadata.Shard); - Assert.Equal("Org: #RedSocks", a1.Metadata.Linked.Shard); + Assert.Equal("Man:Socks", a1.Metadata.Uri); + Assert.Equal("Kids:HappySocks", a1.Metadata.Linked.Uri); Assert.Equal(2, a1.Segments.Count()); Assert.True(a1.Segments.TryGet("key", out string k1)); Assert.Equal("Id", k1); @@ -178,10 +169,8 @@ public async Task Migration_Change_Target_Test() Assert.Equal(MessageOrigin.Copy, a2.Metadata.Origin); Assert.Equal(MessageOrigin.Original, a2.Metadata.Linked.Origin); Assert.Equal("RunAsync", a2.Metadata.Operation); - Assert.Equal("New-Organizations", a2.Metadata.Partition); - Assert.Equal("Organizations", a2.Metadata.Linked.Partition); - Assert.Equal("Org: #WhiteSocks", a2.Metadata.Shard); - Assert.Equal("Org: #RedSocks", a2.Metadata.Linked.Shard); + Assert.Equal("Man:Socks", a2.Metadata.Uri); + Assert.Equal("Kids:HappySocks", a2.Metadata.Linked.Uri); Assert.Equal(2, a1.Segments.Count()); Assert.True(a2.Segments.TryGet("id", out int i2)); Assert.Equal(1, i2); @@ -191,10 +180,8 @@ public async Task Migration_Change_Target_Test() Assert.Equal(MessageOrigin.Copy, a3.Metadata.Origin); Assert.Equal(MessageOrigin.Original, a3.Metadata.Linked.Origin); Assert.Equal("RunAsync", a3.Metadata.Operation); - Assert.Equal("New-Organizations", a3.Metadata.Partition); - Assert.Equal("Organizations", a3.Metadata.Linked.Partition); - Assert.Equal("Org: #WhiteSocks", a3.Metadata.Shard); - Assert.Equal("Org: #RedSocks", a3.Metadata.Linked.Shard); + Assert.Equal("Man:Socks", a3.Metadata.Uri); + Assert.Equal("Kids:HappySocks", a3.Metadata.Linked.Uri); Assert.Equal(2, a1.Segments.Count()); Assert.True(a3.Segments.TryGet("id", out int i3)); Assert.Equal(2, i3); diff --git a/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs b/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs index ba171803..063c4d86 100644 --- a/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs +++ b/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs @@ -61,8 +61,7 @@ public async Task Build_Serializer_Producer_Test() .AddStorageStrategy(l => _producerStorageStrategyA.ToValueTask(), filter: LocalOnlyEmail) .AddStorageStrategy(l => _producerStorageStrategyB.ToValueTask(), filter: LocalAllButEmail) //.WithOptions(producerOption) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .BuildSequenceOperationsProducer(); await producer.RegisterAsync(new User()); @@ -79,8 +78,7 @@ public async Task Build_Serializer_Producer_Test() .AddStorageStrategyFactory(l => _consumerStorageStrategyC.ToValueTask(), EventBucketCategories.Interceptions) //.WithOptions(consumerOptions) .WithCancellation(cts.Token) - .Partition("Organizations") - .Shard("Org: #RedSocks") + .Uri("Kids:HappySocks") .Subscribe(new SequenceOfConsumerBridge(_subscriber)); _ch.Writer.Complete(); diff --git a/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs b/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs index 04fcb3fd..7cb8e1a0 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs @@ -71,9 +71,9 @@ public async ValueTask GetAsync(string eventKey, string env) [HttpGet("more/{partition}/{shard}/{eventKey}/{env?}")] //[AllowAnonymous] [ProducesResponseType(StatusCodes.Status201Created)] - public async ValueTask GetMoreAsync(string partition, string shard, string eventKey, string? env = null) + public async ValueTask GetMoreAsync(string uri, string eventKey, string? env = null) { - var receiver = _baseBuilder.Partition(partition).Shard(shard).Environment(env ?? string.Empty).BuildReceiver(); + var receiver = _baseBuilder.Uri(uri).Environment(env ?? string.Empty).BuildReceiver(); var json = await receiver.GetJsonByIdAsync(eventKey); return json; } diff --git a/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs b/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs index 4ee43b9a..ed17f6c7 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs +++ b/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs @@ -84,7 +84,7 @@ async ValueTask IEventFlowConsumer.Stage1Async(Person PII, string payload) Metadata? meta = ConsumerMetadata.Context; _logger.LogInformation("Consume First Stage {partition} {shard} {PII} {data}", - meta?.Partition, meta?.Shard, PII, payload); + meta?.Uri, PII, payload); await _producer.Stage2Async( JsonDocument.Parse("{\"name\":\"john\"}").RootElement, @@ -95,7 +95,7 @@ ValueTask IEventFlowConsumer.Stage2Async(JsonElement PII, JsonElement data) { var meta = ConsumerMetadata.Context; _logger.LogInformation("Consume 2 Stage {partition} {shard} {PII} {data}", - meta?.Metadata?.Partition, meta?.Metadata?.Shard, PII, data); + meta?.Metadata?.Uri, PII, data); return ValueTask.CompletedTask; } diff --git a/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 4785d415..71e51853 100644 --- a/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -32,8 +32,7 @@ public static IServiceCollection AddEventSource( IEventFlowProducer producer = ProducerBuilder.Empty.UseRedisChannelInjection(ioc) // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .Environment(env) - .Partition(PARTITION) - .Shard("default") + .Uri(PARTITION) .WithLogger(logger) .BuildEventFlowProducer(); return producer; @@ -49,8 +48,7 @@ public static IServiceCollection AddEventSource( OriginFilter = MessageOrigin.Original }) .Environment(env) - .Partition(PARTITION) - .Shard("default"); + .Uri(PARTITION); return consumer; }); services.AddSingleton(ioc => From 11c7dcd9fabdb1e27eb7db903c1e861a5a2d99c5 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 21 May 2023 10:18:02 +0300 Subject: [PATCH 004/178] ignore PR --- .github/workflows/prepare-nuget.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prepare-nuget.yml b/.github/workflows/prepare-nuget.yml index ea49353f..469d0955 100644 --- a/.github/workflows/prepare-nuget.yml +++ b/.github/workflows/prepare-nuget.yml @@ -7,8 +7,8 @@ name: Prepare on: push: branches: [ main ] - pull_request: - branches: [ main ] + # pull_request: + # branches: [ main ] workflow_dispatch: inputs: logLevel: From a680924e6dc731bec7abef2250bf1b658dbd95fa Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 21 May 2023 12:43:47 +0300 Subject: [PATCH 005/178] rfc: re-model --- ...one.Channels.RedisConsumerProvider.csproj} | 6 +- ...bone.Channels.RedisConsumerProvider.csproj | 17 +++++ .../RedisConsumerChannel.cs | 10 +-- .../RedisConsumerProviderExtensions.cs | 6 +- .../Setting/DelayWhenEmptyBehavior.cs | 2 +- .../Setting/RedisConsumerChannelSetting.cs | 2 +- .../Setting/ResiliencePolicies.cs | 2 +- .../Setting/StaleMessagesClaimingTrigger.cs | 2 +- .../RedisHashStorageStrategy.cs | 2 +- .../icon.png | Bin ...one.Channels.RedisProducerProvider.csproj} | 4 +- ...bone.Channels.RedisProducerProvider.csproj | 21 ++++++ .../RedisProducerChannel.cs | 4 +- .../RedisProviderExtensions.cs | 6 +- .../RedisHashStorageStrategy.cs | 2 +- .../icon.png | Bin ...bone.Channels.RedisProvider.Common.csproj} | 2 +- ...kbone.Channels.RedisProvider.Common.csproj | 15 ++++ .../EventSourceRedisConnectionFacroty.cs | 2 +- .../IEventSourceRedisConnectionFacroty.cs | 2 +- .../Factory/IRedisConnectionFacrotyBase.cs | 2 +- .../Factory/RedisClientFactory.cs | 4 +- .../Factory/RedisConnectionFacrotyBase.cs | 2 +- .../Factory/RedisCredentialsKeys.cs | 4 +- .../RedisChannelConstants.cs | 2 +- .../RedisCommonProviderExtensions.cs | 2 +- .../RedisDiExtensions.cs | 2 +- .../RedisTelemetryrExtensions.cs | 2 +- .../icon.png | Bin ...ne.Channels.S3StoreConsumerProvider.csproj | 14 ---- ...ne.Channels.S3StoreProducerProvider.csproj | 9 --- ...ne.Channels.S3StoreConsumerProvider.csproj | 14 ++++ .../S3ConsumerStorageStrategy.cs | 6 +- .../S3ConsumerStorageStrategyExtension.cs | 6 +- .../icon.png | Bin ...ne.Channels.S3StoreProducerProvider.csproj | 9 +++ .../S3ProducerStorageStrategy.cs | 6 +- .../S3ProducerStorageStrategyExtension.cs | 4 +- .../icon.png | Bin .../BlobResponse.cs | 2 +- .../Constants.cs | 2 +- ...ne.Channels.S3StoreProvider.Common.csproj} | 2 +- .../IS3Repository.cs | 2 +- .../IS3RepositoryFactory.cs | 2 +- .../S3EnvironmentConvention.cs | 2 +- .../S3Options.cs | 2 +- .../S3Repository.cs | 4 +- .../S3RepositoryFactory.cs | 2 +- .../icon.png | Bin .../Attributes.cs | 5 -- .../Ack/Ack.cs | 4 +- .../Ack/AckBehavior.cs | 2 +- .../Ack/AckOnce.cs | 2 +- .../Ack/IAck.cs | 2 +- .../Attributes.cs | 5 ++ .../Building/IConsumerFilterBuilder.cs | 2 +- .../Builder/Building/IConsumerHooksBuilder.cs | 4 +- .../Building/IConsumerOptionsBuilder.cs | 2 +- .../Builder/Building/IConsumerReadyBuilder.cs | 4 +- .../IConsumerStorageStrategyWithFilter.cs | 2 +- .../Building/IConsumerStoreStrategyBuilder.cs | 2 +- .../Building/IConsumerSubscribeBuilder.cs | 2 +- .../ConsumerAsyncEnumerableJsonOptions.cs | 2 +- .../ConsumerAsyncEnumerableOptions.cs | 2 +- .../Building/Receiver/IConsumerIterator.cs | 4 +- .../Receiver/IConsumerIteratorCommands.cs | 2 +- .../Building/Receiver/IConsumerReceiver.cs | 4 +- .../Receiver/IConsumerReceiverCommands.cs | 2 +- .../Route/IConsumerEnvironmentBuilder.cs | 2 +- .../Route/IConsumerEnvironmentOfBuilder.cs | 2 +- .../Route/IConsumerPartitionBuilder.cs | 2 +- .../Builder/IConsumer.cs | 2 +- .../Builder/IConsumerBuilder.cs | 4 +- .../Builder/IConsumerLifetime.cs | 4 +- .../Builder/IConsumerPlan.cs | 4 +- .../Builder/IConsumerPlanBase.cs | 2 +- .../Builder/IConsumerPlanBuilder.cs | 4 +- .../ClaimingTrigger.cs | 2 +- .../ConsumerMetadata.cs | 2 +- .../ConsumerOptions.cs | 4 +- .../Enums/MultiConsumerBehavior.cs | 2 +- .../Enums/PartialConsumerBehavior.cs | 2 +- .../EventSourceConsumer.deprecated.cs | 2 +- ...up.Backbone.Consumers.Abstractions.csproj} | 6 +- ...ing.Backbone.Consumers.Abstractions.csproj | 16 +++++ .../IConsumerBridge.cs | 2 +- .../IConsumerEntityMapper.cs | 2 +- .../ISubscriptionBridge.cs | 2 +- .../Interceptors/ConsumerInterceptorBridge.cs | 2 +- .../Interceptors/IConsumerAsyncInterceptor.cs | 4 +- .../Interceptors/IConsumerInterceptor.cs | 4 +- .../Provider/IConsumerChannelProvider.cs | 2 +- .../Provider/IConsumerStorageStrategy.cs | 2 +- .../ConsumerSegmentationStrategyBridge.cs | 2 +- .../IConsumerAsyncSegmenationStrategy.cs | 2 +- .../IConsumerSegmenationStrategy.cs | 2 +- .../TelemetryrExtensions.cs | 2 +- .../icon.png | Bin .../ConsumerBase.EventSourceSubscriber.cs | 8 +-- .../Builder/ConsumerBase.cs | 2 +- .../Builder/ConsumerBuilder.Iterator.cs | 4 +- .../Builder/ConsumerBuilder.Receiver.cs | 4 +- .../Builder/ConsumerBuilder.cs | 6 +- .../Builder/ConsumerPlan.cs | 6 +- .../Builder/FilteredStorageStrategy.cs | 4 +- .../ConsumerDefaultSegmentationStrategy.cs | 4 +- ...urcing - Backup.Backbone.Consumers.csproj} | 6 +- .../EventSourcing.Backbone.Consumers.csproj | 23 +++++++ .../icon.png | Bin .../Attributes/EventSourceGenType.cs | 2 +- .../Attributes/EventSourceVersion.cs | 4 +- .../GenerateEventSourceAttribute.cs | 4 +- .../GenerateEventSourceBaseAttribute.cs | 2 +- .../GenerateEventSourceBridgeAttribute.cs | 2 +- .../Bucket.cs | 4 +- .../BucketJsonConverter.cs | 4 +- .../Entities/Announcement/Announcement.cs | 2 +- .../Entities/Announcement/AnnouncementData.cs | 2 +- .../Announcement/EventSourceJsonContext.cs | 2 +- .../Entities/Announcement/MessageOrigin.cs | 2 +- .../Entities/Announcement/Metadata.cs | 2 +- .../Entities/EventKey/EventKey.cs | 2 +- .../Entities/EventKey/EventKeys.cs | 2 +- .../Enums/EventBucketCategories.cs | 2 +- .../Env.cs | 2 +- .../EventSourceConstants.cs | 2 +- .../EventSourceFallbakLogger.cs | 2 +- .../EventSourceOptions.cs | 2 +- ...cing - Backup.Backbone.Abstractions.csproj | 2 +- ...EventSourcing.Backbone.Abstractions.csproj | 19 ++++++ .../Interfaces/IDataSerializer.cs | 2 +- .../Interfaces/IInterceptorName.cs | 2 +- .../Interfaces/IPlanRoute.cs | 2 +- .../Interfaces/IWithCancellation.cs | 2 +- .../JsonDataSerializer.cs | 4 +- .../TelemetryrExtensions.cs | 2 +- .../icon.png | Bin ...Backbone.sln => EventSourcing.Backbone.sln | 34 +++++----- .../EventSource.Backbone.xml | 2 +- .../EventSourcing - Backup.Backbone.csproj | 2 +- .../EventSourcing.Backbone.csproj | 19 ++++++ .../icon.png | Bin .../EventSource.Backbone.Producers.csproj | 12 ---- .../Building/IProducerBuilderEnvironment.cs | 2 +- .../Building/IProducerEnvironmentBuilder.cs | 2 +- .../Builder/Building/IProducerHooksBuilder.cs | 2 +- .../Building/IProducerLoggerBuilder.cs | 2 +- .../Building/IProducerOptionsBuilder.cs | 2 +- .../Building/IProducerPartitionBuilder.cs | 2 +- .../Builder/Building/IProducerRawBuilder.cs | 2 +- .../Building/IProducerSpecializeBuilder.cs | 2 +- .../IProducerStorageStrategyWithFilter.cs | 2 +- .../Building/IProducerStoreStrategyBuilder.cs | 6 +- .../Builder/Building/IRawProducer.cs | 2 +- .../Override/IProducerOverrideBuildBuilder.cs | 2 +- .../Override/IProducerOverrideBuilder.cs | 4 +- .../IProducerOverrideEnvironmentBuilder.cs | 2 +- .../IProducerOverridePartitionBuilder.cs | 2 +- .../Builder/Building/RawProducerOptions.cs | 2 +- .../Builder/Building/RouteAssignmentType.cs | 2 +- .../Builder/IProducerBuilder.cs | 4 +- ...up.Backbone.Producers.Abstractions.csproj} | 2 +- ...ing.Backbone.Producers.Abstractions.csproj | 12 ++++ .../Interceptors/IProducerAsyncInterceptor.cs | 4 +- .../Interceptors/IProducerInterceptor.cs | 4 +- .../Interceptors/ProducerInterceptorBridge.cs | 2 +- .../Plan/IProducerPlan.cs | 4 +- .../Plan/IProducerPlanBase.cs | 2 +- .../Plan/IProducerPlanBuilder.cs | 6 +- .../Plan/ProducerPlan.cs | 8 +-- .../ProducerExtensions.cs | 6 +- .../ProducerPipeline.cs | 2 +- .../Provider/IProducerChannelProvider.cs | 2 +- .../Provider/IProducerStorageStrategy.cs | 2 +- .../IProducerAsyncSegmentationStrategy.cs | 2 +- .../IProducerSegmentationStrategy.cs | 2 +- .../ProducerSegmentationStrategyBridge.cs | 2 +- .../TelemetryrExtensions.cs | 2 +- .../icon.png | Bin .../Builder/FilteredStorageStrategy.cs | 2 +- .../Builder/ProducerBuilder.cs | 6 +- ...ourcing - Backup.Backbone.Producers.csproj | 12 ++++ .../EventSourcing.Backbone.Producers.csproj | 12 ++++ .../ProducerDefaultSegmentationStrategy.cs | 2 +- .../icon.png | Bin .../Entities/Person.cs | 5 -- ...entSource.Backbone.IntegrationTests.csproj | 64 ------------------ .../Dockerfile.develop | 18 ----- .../.editorconfig | 0 .../Class1.cs | 4 +- .../Contracts/IEventFlow.cs | 2 +- .../Contracts/IEventFlowStage1.cs | 2 +- .../Contracts/IEventFlowStage2.cs | 2 +- .../Contracts/ISequenceOperations.cs | 2 +- .../Contracts}/Inheritance/IFlowA.cs | 2 +- .../Contracts/Inheritance/IFlowAB.cs | 2 +- .../Contracts}/Inheritance/IFlowB.cs | 2 +- .../DeleteKeysTests.cs | 4 +- .../EndToEndExplicitTests.cs | 6 +- .../EndToEndStressTests.cs | 8 +-- .../EndToEndTests.cs | 12 ++-- .../Entities/Person.cs | 5 ++ .../Entities/User.cs | 2 +- ... - Backup.Backbone.IntegrationTests.csproj | 64 ++++++++++++++++++ ...tSourcing.Backbone.IntegrationTests.csproj | 64 ++++++++++++++++++ .../InheritanceS3StoreStrategyTests.cs | 2 +- .../InheritanceTests.cs | 8 +-- .../MigrationReceiverTest.cs | 4 +- .../MigrationTest.cs | 10 +-- .../S3StoreStrategyExplicitTests.cs | 2 +- .../S3StoreStrategyStressTests .cs | 2 +- .../S3StoreStrategyTests.cs | 2 +- .../icon.png | Bin .../payload.json | 0 .../person.json | 0 .../.editorconfig | 0 .../EventSourceApiConsumerDesignTests.cs | 4 +- .../EventSourceApiProducerDesignTests.cs | 6 +- .../ApiDesign/TestApiExtensions.cs | 4 +- .../Channels/ConsumerTestChannel.cs | 2 +- .../Channels/ProducerTestChannel.cs | 4 +- .../ConsumerBuilderTests.cs | 4 +- .../Contracts/ISequenceOperations.cs | 2 +- .../Contracts/ISimpleEvent.cs | 2 +- .../Contracts/ISimpleEventTag.cs | 2 +- .../EndToEndTests.cs | 6 +- .../Entities/User.cs | 2 +- .../EventIdTests.cs | 2 +- ...urcing - Backup.Backbone.UnitTests.csproj} | 16 ++--- .../EventSourcing.Backbone.UnitTests.csproj | 46 +++++++++++++ .../SequenceOfProducerFactory.cs | 2 +- .../SequenceOfProducerFactoryExtensions.cs | 4 +- .../SequenceOperationsConsumer.cs | 2 +- .../SequenceOperationsProducerFactory.cs | 2 +- ...enceOperationsProducerFactoryExtensions.cs | 4 +- .../InheritGenerationTest.cs | 4 +- .../ProducerBuilderTests.cs | 6 +- .../S3OptionsTests.cs | 2 +- .../SerializationTests.cs | 2 +- .../SourceMigrationTests.cs | 6 +- .../StoreStrategyTests.cs | 6 +- .../Subscriptions/SimpleEventSubscription.cs | 6 +- .../SimpleEventSubscriptionBase.cs | 4 +- .../SimpleEventSubscriptionBridge.cs | 6 +- ...SimpleEventSubscriptionBridgeExtensions.cs | 6 +- .../SimpleEventSubscriptionFromGen.cs | 6 +- .../icon.png | Bin .../.editorconfig | 0 .../AspCoreExtensions.cs | 2 +- .../Contracts/IEventFlow.cs | 6 +- .../Contracts/IEventsMigration.cs | 2 +- .../Contracts/IVersionedEvents.cs | 2 +- .../Controllers/EventSourceApiController.cs | 2 +- .../Controllers/MigrationController.cs | 2 +- .../Controllers/TestController.cs | 2 +- .../Dockerfile | 0 .../Dockerfile.develop | 18 +++++ .../Entities/Address.cs | 2 +- .../Entities/Person.cs | 2 +- ...ing - Backup.Backbone.WebEventTest.csproj} | 14 ++-- ...EventSourcing.Backbone.WebEventTest.csproj | 44 ++++++++++++ .../Jobs/HostedServiceBase.cs | 0 .../Jobs/MicroDemoJob.cs | 4 +- .../Jobs/MigrationJob.cs | 4 +- .../Program.cs | 4 +- .../Properties/launchSettings.json | 0 .../RegisterEventSourceExtensions.cs | 4 +- .../TestSampler.cs | 0 .../appsettings.Development.json | 0 .../appsettings.json | 0 .../azds.yaml | 2 +- .../.helmignore | 0 .../Chart.yaml | 0 .../templates/NOTES.txt | 0 .../templates/_helpers.tpl | 0 .../templates/deployment.yaml | 0 .../templates/ingress.yaml | 0 .../templates/secrets.yaml | 0 .../templates/service.yaml | 0 .../values.yaml | 0 .../icon.png | Bin ...ntSource.Backbone.SrcGen.Playground.csproj | 24 ------- .../Properties/launchSettings.json | 8 --- ...Sourcing.Backbone.SrcGen.Playground.csproj | 24 +++++++ .../ISample.cs | 2 +- .../Inheritance/IFlowA.cs | 2 +- .../Inheritance/IFlowAB.cs | 2 +- .../Inheritance/IFlowB.cs | 2 +- .../Program.cs | 2 +- .../icon.png | Bin ...tSourcing - Backup.Backbone.SrcGen.csproj} | 2 +- .../EventSourcing.Backbone.SrcGen.csproj | 38 +++++++++++ .../Concrete/BridgeIncrementalGenerator.cs | 8 +-- .../Concrete/ContractIncrementalGenerator.cs | 6 +- .../EntitiesAndHelpers/EntityGenerator.cs | 6 +- .../EntitiesAndHelpers/GenInstruction.cs | 2 +- .../EntitiesAndHelpers/KindFilter.cs | 2 +- .../Generators/GeneratorIncrementalBase.cs | 12 ++-- .../Helper.cs | 2 +- .../Properties/launchSettings.json | 8 +++ .../RoslynHelper.cs | 0 .../SyntaxReceiverResult.cs | 2 +- .../icon.png | Bin 303 files changed, 902 insertions(+), 556 deletions(-) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj => EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj} (51%) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/RedisConsumerChannel.cs (99%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/RedisConsumerProviderExtensions.cs (97%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/Setting/DelayWhenEmptyBehavior.cs (94%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/Setting/RedisConsumerChannelSetting.cs (95%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/Setting/ResiliencePolicies.cs (98%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/Setting/StaleMessagesClaimingTrigger.cs (95%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/StorageStrategies/RedisHashStorageStrategy.cs (98%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisConsumerProvider => EventSourcing.Backbone.Channels.RedisConsumerProvider}/icon.png (100%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProducerProvider/EventSource.Backbone.Channels.RedisProducerProvider.csproj => EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj} (63%) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProducerProvider => EventSourcing.Backbone.Channels.RedisProducerProvider}/RedisProducerChannel.cs (98%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProducerProvider => EventSourcing.Backbone.Channels.RedisProducerProvider}/RedisProviderExtensions.cs (97%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProducerProvider => EventSourcing.Backbone.Channels.RedisProducerProvider}/StorageStrategies/RedisHashStorageStrategy.cs (98%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProducerProvider => EventSourcing.Backbone.Channels.RedisProducerProvider}/icon.png (100%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common/EventSource.Backbone.Channels.RedisProvider.Common.csproj => EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj} (79%) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/Factory/EventSourceRedisConnectionFacroty.cs (98%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/Factory/IEventSourceRedisConnectionFacroty.cs (82%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/Factory/IRedisConnectionFacrotyBase.cs (92%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/Factory/RedisClientFactory.cs (98%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/Factory/RedisConnectionFacrotyBase.cs (99%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/Factory/RedisCredentialsKeys.cs (81%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/RedisChannelConstants.cs (93%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/RedisCommonProviderExtensions.cs (99%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/RedisDiExtensions.cs (95%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/RedisTelemetryrExtensions.cs (97%) rename Channels/REDIS/{EventSource.Backbone.Channels.RedisProvider.Common => EventSourcing.Backbone.Channels.RedisProvider.Common}/icon.png (100%) delete mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj delete mode 100644 Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/EventSource.Backbone.Channels.S3StoreProducerProvider.csproj create mode 100644 Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj rename Channels/S3/{EventSource.Backbone.Channels.S3StoreConsumerProvider => EventSourcing.Backbone.Channels.S3StoreConsumerProvider}/S3ConsumerStorageStrategy.cs (96%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreConsumerProvider => EventSourcing.Backbone.Channels.S3StoreConsumerProvider}/S3ConsumerStorageStrategyExtension.cs (91%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreConsumerProvider => EventSourcing.Backbone.Channels.S3StoreConsumerProvider}/icon.png (100%) create mode 100644 Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProducerProvider => EventSourcing.Backbone.Channels.S3StoreProducerProvider}/S3ProducerStorageStrategy.cs (96%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProducerProvider => EventSourcing.Backbone.Channels.S3StoreProducerProvider}/S3ProducerStorageStrategyExtension.cs (96%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProducerProvider => EventSourcing.Backbone.Channels.S3StoreProducerProvider}/icon.png (100%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/BlobResponse.cs (99%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/Constants.cs (77%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj => EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj} (73%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/IS3Repository.cs (98%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/IS3RepositoryFactory.cs (85%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/S3EnvironmentConvention.cs (92%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/S3Options.cs (96%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/S3Repository.cs (99%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/S3RepositoryFactory.cs (98%) rename Channels/S3/{EventSource.Backbone.Channels.S3StoreProvider.Common => EventSourcing.Backbone.Channels.S3StoreProvider.Common}/icon.png (100%) delete mode 100644 Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Ack/Ack.cs (97%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Ack/AckBehavior.cs (92%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Ack/AckOnce.cs (99%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Ack/IAck.cs (94%) create mode 100644 Consumers/EventSourcing.Backbone.Consumers.Abstractions/Attributes.cs rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerFilterBuilder.cs (97%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerHooksBuilder.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerOptionsBuilder.cs (90%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerReadyBuilder.cs (90%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerStorageStrategyWithFilter.cs (94%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerStoreStrategyBuilder.cs (95%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/IConsumerSubscribeBuilder.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs (89%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs (96%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Receiver/IConsumerIterator.cs (92%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Receiver/IConsumerIteratorCommands.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Receiver/IConsumerReceiver.cs (79%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Receiver/IConsumerReceiverCommands.cs (96%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Route/IConsumerEnvironmentBuilder.cs (86%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs (92%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/Building/Route/IConsumerPartitionBuilder.cs (90%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/IConsumer.cs (86%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/IConsumerBuilder.cs (86%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/IConsumerLifetime.cs (75%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/IConsumerPlan.cs (95%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/IConsumerPlanBase.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Builder/IConsumerPlanBuilder.cs (91%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/ClaimingTrigger.cs (96%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/ConsumerMetadata.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/ConsumerOptions.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Enums/MultiConsumerBehavior.cs (96%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Enums/PartialConsumerBehavior.cs (95%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/EventSourceConsumer.deprecated.cs (97%) rename Consumers/{EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj => EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj} (50%) create mode 100644 Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/IConsumerBridge.cs (94%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/IConsumerEntityMapper.cs (94%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/ISubscriptionBridge.cs (94%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Interceptors/ConsumerInterceptorBridge.cs (97%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Interceptors/IConsumerAsyncInterceptor.cs (88%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Interceptors/IConsumerInterceptor.cs (87%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Provider/IConsumerChannelProvider.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Provider/IConsumerStorageStrategy.cs (97%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Segmentation/ConsumerSegmentationStrategyBridge.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Segmentation/IConsumerAsyncSegmenationStrategy.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/Segmentation/IConsumerSegmenationStrategy.cs (98%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/TelemetryrExtensions.cs (97%) rename Consumers/{EventSource.Backbone.Consumers.Contracts => EventSourcing.Backbone.Consumers.Abstractions}/icon.png (100%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/ConsumerBase.EventSourceSubscriber.cs (98%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/ConsumerBase.cs (98%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/ConsumerBuilder.Iterator.cs (99%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/ConsumerBuilder.Receiver.cs (98%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/ConsumerBuilder.cs (99%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/ConsumerPlan.cs (99%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/Builder/FilteredStorageStrategy.cs (96%) rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/ConsumerDefaultSegmentationStrategy.cs (95%) rename Consumers/{EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj => EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj} (62%) create mode 100644 Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj rename Consumers/{EventSource.Backbone.Consumers => EventSourcing.Backbone.Consumers}/icon.png (100%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Attributes/EventSourceGenType.cs (81%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Attributes/EventSourceVersion.cs (92%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Attributes/GenerateEventSourceAttribute.cs (82%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Attributes/GenerateEventSourceBaseAttribute.cs (97%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Attributes/GenerateEventSourceBridgeAttribute.cs (92%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Bucket.cs (99%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/BucketJsonConverter.cs (96%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/Announcement/Announcement.cs (97%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/Announcement/AnnouncementData.cs (98%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/Announcement/EventSourceJsonContext.cs (88%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/Announcement/MessageOrigin.cs (91%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/Announcement/Metadata.cs (99%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/EventKey/EventKey.cs (98%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Entities/EventKey/EventKeys.cs (99%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Enums/EventBucketCategories.cs (86%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Env.cs (98%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/EventSourceConstants.cs (97%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/EventSourceFallbakLogger.cs (98%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/EventSourceOptions.cs (98%) rename EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj => EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj (92%) create mode 100644 EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Interfaces/IDataSerializer.cs (94%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Interfaces/IInterceptorName.cs (89%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Interfaces/IPlanRoute.cs (92%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/Interfaces/IWithCancellation.cs (91%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/JsonDataSerializer.cs (92%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/TelemetryrExtensions.cs (97%) rename {EventSource.Backbone.Contracts => EventSourcing.Backbone.Abstractions}/icon.png (100%) rename EventSource.Backbone.sln => EventSourcing.Backbone.sln (71%) rename {EventSource.Backbone => EventSourcing.Backbone}/EventSource.Backbone.xml (68%) rename EventSource.Backbone/EventSource.Backbone.csproj => EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj (77%) create mode 100644 EventSourcing.Backbone/EventSourcing.Backbone.csproj rename {EventSource.Backbone => EventSourcing.Backbone}/icon.png (100%) delete mode 100644 Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerBuilderEnvironment.cs (90%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerEnvironmentBuilder.cs (85%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerHooksBuilder.cs (99%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerLoggerBuilder.cs (93%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerOptionsBuilder.cs (89%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerPartitionBuilder.cs (92%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerRawBuilder.cs (90%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerSpecializeBuilder.cs (97%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerStorageStrategyWithFilter.cs (95%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IProducerStoreStrategyBuilder.cs (87%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/IRawProducer.cs (91%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/Override/IProducerOverrideBuildBuilder.cs (95%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/Override/IProducerOverrideBuilder.cs (91%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs (93%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/Override/IProducerOverridePartitionBuilder.cs (94%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/RawProducerOptions.cs (90%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/Building/RouteAssignmentType.cs (83%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Builder/IProducerBuilder.cs (93%) rename Producers/{EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj => EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj} (72%) create mode 100644 Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Interceptors/IProducerAsyncInterceptor.cs (89%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Interceptors/IProducerInterceptor.cs (86%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Interceptors/ProducerInterceptorBridge.cs (97%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Plan/IProducerPlan.cs (90%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Plan/IProducerPlanBase.cs (97%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Plan/IProducerPlanBuilder.cs (89%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Plan/ProducerPlan.cs (98%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/ProducerExtensions.cs (95%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/ProducerPipeline.cs (99%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Provider/IProducerChannelProvider.cs (95%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Provider/IProducerStorageStrategy.cs (97%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Segmentation/IProducerAsyncSegmentationStrategy.cs (98%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Segmentation/IProducerSegmentationStrategy.cs (97%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/Segmentation/ProducerSegmentationStrategyBridge.cs (98%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/TelemetryrExtensions.cs (98%) rename Producers/{EventSource.Backbone.Producers.Contracts => EventSourcing.Backbone.Producers.Abstractions}/icon.png (100%) rename Producers/{EventSource.Backbone.Producers => EventSourcing.Backbone.Producers}/Builder/FilteredStorageStrategy.cs (98%) rename Producers/{EventSource.Backbone.Producers => EventSourcing.Backbone.Producers}/Builder/ProducerBuilder.cs (99%) create mode 100644 Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj create mode 100644 Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj rename Producers/{EventSource.Backbone.Producers => EventSourcing.Backbone.Producers}/ProducerDefaultSegmentationStrategy.cs (94%) rename Producers/{EventSource.Backbone.Producers => EventSourcing.Backbone.Producers}/icon.png (100%) delete mode 100644 Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs delete mode 100644 Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj delete mode 100644 Tests/EventSource.Backbone.WebEventTest/Dockerfile.develop rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/.editorconfig (100%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Class1.cs (85%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Contracts/IEventFlow.cs (93%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Contracts/IEventFlowStage1.cs (87%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Contracts/IEventFlowStage2.cs (90%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Contracts/ISequenceOperations.cs (95%) rename {src-gen/EventSource.Backbone.SrcGen.Playground => Tests/EventSourcing.Backbone.IntegrationTests/Contracts}/Inheritance/IFlowA.cs (74%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Contracts/Inheritance/IFlowAB.cs (77%) rename {src-gen/EventSource.Backbone.SrcGen.Playground => Tests/EventSourcing.Backbone.IntegrationTests/Contracts}/Inheritance/IFlowB.cs (75%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/DeleteKeysTests.cs (94%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/EndToEndExplicitTests.cs (98%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/EndToEndStressTests.cs (98%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/EndToEndTests.cs (99%) create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/Entities/Person.cs rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/Entities/User.cs (94%) create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/InheritanceS3StoreStrategyTests.cs (93%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/InheritanceTests.cs (98%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/MigrationReceiverTest.cs (98%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/MigrationTest.cs (98%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/S3StoreStrategyExplicitTests.cs (89%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/S3StoreStrategyStressTests .cs (93%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/S3StoreStrategyTests.cs (93%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/icon.png (100%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/payload.json (100%) rename Tests/{EventSource.Backbone.IntegrationTests => EventSourcing.Backbone.IntegrationTests}/person.json (100%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/.editorconfig (100%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/ApiDesign/EventSourceApiConsumerDesignTests.cs (95%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/ApiDesign/EventSourceApiProducerDesignTests.cs (97%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/ApiDesign/TestApiExtensions.cs (97%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Channels/ConsumerTestChannel.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Channels/ProducerTestChannel.cs (92%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/ConsumerBuilderTests.cs (95%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Contracts/ISequenceOperations.cs (93%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Contracts/ISimpleEvent.cs (94%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Contracts/ISimpleEventTag.cs (89%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/EndToEndTests.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Entities/User.cs (95%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/EventIdTests.cs (97%) rename Tests/{EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj => EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj} (52%) create mode 100644 Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs (85%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Factory/SequenceOperations/SequenceOperationsConsumer.cs (95%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs (79%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/InheritGenerationTest.cs (88%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/ProducerBuilderTests.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/S3OptionsTests.cs (99%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/SerializationTests.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/SourceMigrationTests.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/StoreStrategyTests.cs (98%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Subscriptions/SimpleEventSubscription.cs (79%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionBase.cs (95%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionBridge.cs (91%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs (74%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/Subscriptions/SimpleEventSubscriptionFromGen.cs (82%) rename Tests/{EventSource.Backbone.UnitTests => EventSourcing.Backbone.UnitTests}/icon.png (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/.editorconfig (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/AspCoreExtensions.cs (99%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Contracts/IEventFlow.cs (88%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Contracts/IEventsMigration.cs (91%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Contracts/IVersionedEvents.cs (96%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Controllers/EventSourceApiController.cs (98%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Controllers/MigrationController.cs (94%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Controllers/TestController.cs (97%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Dockerfile (100%) create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Entities/Address.cs (60%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Entities/Person.cs (61%) rename Tests/{EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj => EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj} (56%) create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Jobs/HostedServiceBase.cs (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Jobs/MicroDemoJob.cs (97%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Jobs/MigrationJob.cs (96%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Program.cs (98%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/Properties/launchSettings.json (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/RegisterEventSourceExtensions.cs (97%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/TestSampler.cs (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/appsettings.Development.json (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/appsettings.json (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/azds.yaml (95%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/.helmignore (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/charts/weknoweventsourcebackbonewebeventtest/values.yaml (100%) rename Tests/{EventSource.Backbone.WebEventTest => EventSourcing.Backbone.WebEventTest}/icon.png (100%) delete mode 100644 src-gen/EventSource.Backbone.SrcGen.Playground/EventSource.Backbone.SrcGen.Playground.csproj delete mode 100644 src-gen/EventSource.Backbone.SrcGen/Properties/launchSettings.json create mode 100644 src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj rename src-gen/{EventSource.Backbone.SrcGen.Playground => EventSourcing.Backbone.SrcGen.Playground}/ISample.cs (89%) rename {Tests/EventSource.Backbone.IntegrationTests/Contracts => src-gen/EventSourcing.Backbone.SrcGen.Playground}/Inheritance/IFlowA.cs (74%) rename src-gen/{EventSource.Backbone.SrcGen.Playground => EventSourcing.Backbone.SrcGen.Playground}/Inheritance/IFlowAB.cs (72%) rename {Tests/EventSource.Backbone.IntegrationTests/Contracts => src-gen/EventSourcing.Backbone.SrcGen.Playground}/Inheritance/IFlowB.cs (75%) rename src-gen/{EventSource.Backbone.SrcGen.Playground => EventSourcing.Backbone.SrcGen.Playground}/Program.cs (74%) rename src-gen/{EventSource.Backbone.SrcGen.Playground => EventSourcing.Backbone.SrcGen.Playground}/icon.png (100%) rename src-gen/{EventSource.Backbone.SrcGen/EventSource.Backbone.SrcGen.csproj => EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj} (94%) create mode 100644 src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Generators/Concrete/BridgeIncrementalGenerator.cs (98%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Generators/Concrete/ContractIncrementalGenerator.cs (95%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Generators/EntitiesAndHelpers/EntityGenerator.cs (98%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Generators/EntitiesAndHelpers/GenInstruction.cs (92%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Generators/EntitiesAndHelpers/KindFilter.cs (59%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Generators/GeneratorIncrementalBase.cs (96%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/Helper.cs (98%) create mode 100644 src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/RoslynHelper.cs (100%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/SyntaxReceiverResult.cs (99%) rename src-gen/{EventSource.Backbone.SrcGen => EventSourcing.Backbone.SrcGen}/icon.png (100%) diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj similarity index 51% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj index a47e03fa..739e3a70 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/EventSource.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj @@ -1,10 +1,10 @@  - + - - + + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj new file mode 100644 index 00000000..f80699c6 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs similarity index 99% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 63c12415..2f53e5cc 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -7,16 +7,16 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.Channels.RedisProvider.Common; -using EventSource.Backbone.Private; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider.Common; +using EventSourcing.Backbone.Private; using static System.Math; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // TODO: [bnaya 2021-07] MOVE TELEMETRY TO THE BASE CLASSES OF PRODUCER / CONSUME -namespace EventSource.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider { /// /// The redis consumer channel. diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs similarity index 97% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs index 2d41840d..83857532 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs @@ -4,10 +4,10 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.Channels.RedisProvider; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class RedisConsumerProviderExtensions { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs similarity index 94% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs index 2b0b4001..b0b0a764 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs @@ -1,7 +1,7 @@ using static System.Math; -namespace EventSource.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider { /// /// Behavior of delay when empty diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs similarity index 95% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs index 805a723d..b2ce638a 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/RedisConsumerChannelSetting.cs @@ -1,7 +1,7 @@ using Polly; -namespace EventSource.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider { /// /// Represent specific setting of the consumer channel diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs similarity index 98% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs index 51793ec9..3daad743 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/ResiliencePolicies.cs @@ -1,7 +1,7 @@ using Polly; using Polly.CircuitBreaker; -namespace EventSource.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider { /// /// Define when to claim stale (long waiting) messages from other consumers diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs similarity index 95% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs index b2c6d125..a32babbc 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/StaleMessagesClaimingTrigger.cs @@ -1,6 +1,6 @@ // TODO: [bnaya 2021-02] use Record -namespace EventSource.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider { /// /// Define when to claim stale (long waiting) messages from other consumers diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs similarity index 98% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 815d874e..ba9d1e25 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -1,6 +1,6 @@ using StackExchange.Redis; -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// /// Responsible to save information to REDIS hash storage. diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/icon.png b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/icon.png similarity index 100% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisConsumerProvider/icon.png rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/icon.png diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/EventSource.Backbone.Channels.RedisProducerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj similarity index 63% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/EventSource.Backbone.Channels.RedisProducerProvider.csproj rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj index 9f3c8652..97d59326 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/EventSource.Backbone.Channels.RedisProducerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj new file mode 100644 index 00000000..06906d64 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs similarity index 98% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index dc1a6d23..5aa91799 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -10,9 +10,9 @@ using StackExchange.Redis; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace EventSource.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider { internal class RedisProducerChannel : IProducerChannelProvider { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs similarity index 97% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs index 060970bf..12275647 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs @@ -8,10 +8,10 @@ using StackExchange.Redis; -using EventSource.Backbone.Channels.RedisProvider; -using EventSource.Backbone.Private; +using EventSourcing.Backbone.Channels.RedisProvider; +using EventSourcing.Backbone.Private; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class RedisProviderExtensions { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs similarity index 98% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 121ecde7..8711dc68 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -4,7 +4,7 @@ using StackExchange.Redis; -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// /// Responsible to save information to REDIS hash storage. diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/icon.png b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/icon.png similarity index 100% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProducerProvider/icon.png rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/icon.png diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/EventSource.Backbone.Channels.RedisProvider.Common.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj similarity index 79% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/EventSource.Backbone.Channels.RedisProvider.Common.csproj rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj index c86dc45a..b710ddb9 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/EventSource.Backbone.Channels.RedisProvider.Common.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj @@ -1,7 +1,7 @@  - + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj new file mode 100644 index 00000000..63dacac8 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs similarity index 98% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs index 0c329393..8e994f7c 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs @@ -3,7 +3,7 @@ using StackExchange.Redis; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source connection (for IoC) diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs similarity index 82% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs index 45ba44ab..f66e0ae5 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Connection factory diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs similarity index 92% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs index a9ba7ef9..f0c6f104 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs @@ -1,6 +1,6 @@ using StackExchange.Redis; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Connection factory diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs similarity index 98% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs index f926f05e..6525868f 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs @@ -5,9 +5,9 @@ using StackExchange.Redis; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// REDIS client factory diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs similarity index 99% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs index ff9ab3ba..3d07d689 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs @@ -3,7 +3,7 @@ using StackExchange.Redis; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source connection (for IoC) diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs similarity index 81% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs index 5cbe23d9..4812f0b6 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs @@ -1,7 +1,7 @@ -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Environment keys for REDIS's credentials diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs similarity index 93% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs index 2e110af9..5ae8f16c 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Channels.RedisProvider.Common +namespace EventSourcing.Backbone.Channels.RedisProvider.Common { public static class RedisChannelConstants { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs similarity index 99% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index d6160a26..607b2e5a 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -2,7 +2,7 @@ using StackExchange.Redis; -namespace EventSource.Backbone.Private +namespace EventSourcing.Backbone.Private { /// /// Redis common provider extensions diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs similarity index 95% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs index 59e9bec2..eeac12be 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; -using EventSource.Backbone; +using EventSourcing.Backbone; namespace Microsoft.Extensions.Configuration { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs similarity index 97% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs index a9bb9771..f57a94c0 100644 --- a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class RedisTelemetryrExtensions { diff --git a/Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/icon.png b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/icon.png similarity index 100% rename from Channels/REDIS/EventSource.Backbone.Channels.RedisProvider.Common/icon.png rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/icon.png diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj deleted file mode 100644 index ca9fd8c9..00000000 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/EventSource.Backbone.Channels.S3StoreProducerProvider.csproj b/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/EventSource.Backbone.Channels.S3StoreProducerProvider.csproj deleted file mode 100644 index 16289dc0..00000000 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/EventSource.Backbone.Channels.S3StoreProducerProvider.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj new file mode 100644 index 00000000..494e59d6 --- /dev/null +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs similarity index 96% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs index 70f3eb81..8082fedc 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs @@ -3,12 +3,12 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Channels; +using EventSourcing.Backbone.Channels; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.EventSourceConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible to load information from S3 storage. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs similarity index 91% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 8b4d2274..5c56a67f 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -1,11 +1,11 @@  using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.Channels; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Extension methods for S3 storage strategy. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/icon.png similarity index 100% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreConsumerProvider/icon.png rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/icon.png diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj new file mode 100644 index 00000000..98a7414c --- /dev/null +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs similarity index 96% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs index d9d74c48..f5f86ab6 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs @@ -3,12 +3,12 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Channels; +using EventSourcing.Backbone.Channels; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.EventSourceConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible to save information to S3 storage. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs similarity index 96% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index db5a4aa2..7c0ff25c 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -1,10 +1,10 @@  using Microsoft.Extensions.Logging; -using EventSource.Backbone.Channels; +using EventSourcing.Backbone.Channels; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Extension methods for S3 storage strategy. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/icon.png similarity index 100% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProducerProvider/icon.png rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/icon.png diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs similarity index 99% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs index 3675fb96..77cee2dd 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// /// Response structure diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/Constants.cs similarity index 77% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/Constants.cs index c90260ee..a6f4ac4c 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/Constants.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/Constants.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// /// Constants diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj similarity index 73% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index b05f5a1e..5eab683f 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/EventSource.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -1,7 +1,7 @@  - + diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs similarity index 98% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs index c8254432..2a527832 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3Repository.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; using System.Text.Json; -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// /// The S3 repository contract. diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs similarity index 85% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs index 86476282..c899b1f9 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { public interface IS3RepositoryFactory { diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs similarity index 92% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs index 06b7debe..6560025f 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Environment convention's options diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs similarity index 96% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs index d5b78e76..f0fc97c3 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Options.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// S3 provider options diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs similarity index 99% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index bd2ec6c0..ee0d3655 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -7,9 +7,9 @@ using Microsoft.Extensions.Logging; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.EventSourceConstants; -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs similarity index 98% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index 78f27eda..5ef5a82f 100644 --- a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; -namespace EventSource.Backbone.Channels +namespace EventSourcing.Backbone.Channels { /// /// Abstract S3 operations diff --git a/Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/icon.png similarity index 100% rename from Channels/S3/EventSource.Backbone.Channels.S3StoreProvider.Common/icon.png rename to Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/icon.png diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs b/Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs deleted file mode 100644 index 1e983d1e..00000000 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Attributes.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; - - -[assembly: InternalsVisibleToAttribute("EventSource.Backbone.Consumers")] - diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/Ack.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/Ack.cs similarity index 97% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Ack/Ack.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/Ack.cs index 601282bf..b2badabc 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/Ack.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/Ack.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Acknowledge context @@ -49,7 +49,7 @@ public static IAsyncDisposable Set(IAck ack) /// /// Empty implementation /// - /// + /// private readonly struct NOP : IAck { public static readonly IAck Default = new NOP(); diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckBehavior.cs similarity index 92% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckBehavior.cs index c2c3aba8..ee84333e 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckBehavior.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckBehavior.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public enum AckBehavior { diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckOnce.cs similarity index 99% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckOnce.cs index f846291b..43d44a47 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/AckOnce.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckOnce.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Preform acknowledge (which should prevent the diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/IAck.cs similarity index 94% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/IAck.cs index 9c04e80b..487b2635 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Ack/IAck.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/IAck.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Preform acknowledge (which should prevent the diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Attributes.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Attributes.cs new file mode 100644 index 00000000..4ffe53a9 --- /dev/null +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Attributes.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + + +[assembly: InternalsVisibleToAttribute("EventSourcing.Backbone.Consumers")] + diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerFilterBuilder.cs similarity index 97% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerFilterBuilder.cs index 61418a99..25bb3cb6 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerFilterBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerFilterBuilder.cs @@ -1,6 +1,6 @@ using System.Collections; -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs index 2d3c177c..27a03650 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerHooksBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source producer builder. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerOptionsBuilder.cs similarity index 90% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerOptionsBuilder.cs index 4461eb69..51f5fd42 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerOptionsBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Enable configuration. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs similarity index 90% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs index c2e84406..2c04f91c 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerReadyBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs @@ -2,9 +2,9 @@ using Polly; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source producer builder. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStorageStrategyWithFilter.cs similarity index 94% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStorageStrategyWithFilter.cs index 469681e8..bf894a19 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStorageStrategyWithFilter.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStorageStrategyWithFilter.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Responsible to load information from storage. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStoreStrategyBuilder.cs similarity index 95% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStoreStrategyBuilder.cs index dc64fa24..c91b5fb3 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerStoreStrategyBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStoreStrategyBuilder.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging; -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Enable configuration. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs index 1a60fdb1..40b96e16 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/IConsumerSubscribeBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs similarity index 89% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs index 8390bd08..af72ae3e 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs similarity index 96% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs index 194c5ff4..6eea06b0 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs similarity index 92% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs index e089f064..9d212481 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIterator.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIteratorCommands.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIteratorCommands.cs index d7a42125..ea21ef24 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerIteratorCommands.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIteratorCommands.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs similarity index 79% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs index 30a85a57..f4aa9bb7 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiver.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiverCommands.cs similarity index 96% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiverCommands.cs index 46e8d25f..365c9e3e 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Receiver/IConsumerReceiverCommands.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiverCommands.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Receive data (on demand data query). diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs similarity index 86% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs index d4c2b784..c0828daa 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs similarity index 92% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs index 6b4bba83..7a16f5ea 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Event Source producer builder. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerPartitionBuilder.cs similarity index 90% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerPartitionBuilder.cs index ff743540..cd0f1c1b 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/Building/Route/IConsumerPartitionBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerPartitionBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumer.cs similarity index 86% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumer.cs index 694b8aab..e3bdd001 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumer.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumer.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs similarity index 86% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs index b95a9dbc..f2dfb1df 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source Consumer builder. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerLifetime.cs similarity index 75% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerLifetime.cs index 65bb6a93..8d9a2ee7 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerLifetime.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerLifetime.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public interface IConsumerLifetime : IConsumerSubscribtionHubBuilder, IAsyncDisposable { diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs similarity index 95% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs index 5f3b5493..d19cd746 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs @@ -1,8 +1,8 @@ using System.Collections.Immutable; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBase.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBase.cs index 88e97e30..b69dc1eb 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBase.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBase.cs @@ -4,7 +4,7 @@ using Polly; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs similarity index 91% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs index 7ef1fee0..b9c73b1e 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Builder/IConsumerPlanBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs @@ -2,9 +2,9 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Building phase of the plan diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ClaimingTrigger.cs similarity index 96% rename from Consumers/EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/ClaimingTrigger.cs index 28ff8b3d..2f78886c 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/ClaimingTrigger.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ClaimingTrigger.cs @@ -1,6 +1,6 @@ // TODO: [bnaya 2021-02] use Record -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Define when to claim stale (long waiting) messages from other consumers diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs index 7c35e1f0..86b2824e 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerMetadata.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Represent metadata of message (command / event) metadata of diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs index 86c14dd1..e62e53ea 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/ConsumerOptions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Enums; +using EventSourcing.Backbone.Enums; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public record ConsumerOptions : EventSourceOptions diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/MultiConsumerBehavior.cs similarity index 96% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/MultiConsumerBehavior.cs index 04f34e8a..f022d7ca 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/MultiConsumerBehavior.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/MultiConsumerBehavior.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Enums +namespace EventSourcing.Backbone.Enums { /// /// Collaborate behavior of multi consumers registered via common subscription object. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/PartialConsumerBehavior.cs similarity index 95% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/PartialConsumerBehavior.cs index eb0e282e..263f969f 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Enums/PartialConsumerBehavior.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/PartialConsumerBehavior.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Enums +namespace EventSourcing.Backbone.Enums { /// /// Gets or sets the partial behavior diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourceConsumer.deprecated.cs similarity index 97% rename from Consumers/EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourceConsumer.deprecated.cs index 4f6e180e..7dc5e1f2 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/EventSourceConsumer.deprecated.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourceConsumer.deprecated.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Base class for the consumer's code generator diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj similarity index 50% rename from Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj index 6869da0b..6ddce0ff 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/EventSource.Backbone.Consumers.Contracts.csproj +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj @@ -2,13 +2,13 @@ - EventSource.Backbone + EventSourcing.Backbone - + - + diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj new file mode 100644 index 00000000..66f56dea --- /dev/null +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj @@ -0,0 +1,16 @@ + + + + + EventSourcing.Backbone + + + + + + + + + + + diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerBridge.cs similarity index 94% rename from Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerBridge.cs index 3089a39f..54b5c7fb 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerBridge.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerBridge.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible of translating announcement's parameter into object diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerEntityMapper.cs similarity index 94% rename from Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerEntityMapper.cs index 2a693b7f..953c3ee7 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/IConsumerEntityMapper.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerEntityMapper.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible of translating announcement's parameter into object diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ISubscriptionBridge.cs similarity index 94% rename from Consumers/EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/ISubscriptionBridge.cs index f2843e7b..4855af73 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/ISubscriptionBridge.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ISubscriptionBridge.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Subscription Bridge convention diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/ConsumerInterceptorBridge.cs similarity index 97% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/ConsumerInterceptorBridge.cs index 8065661b..bb15aa01 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/ConsumerInterceptorBridge.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/ConsumerInterceptorBridge.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Bridge segmentation diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerAsyncInterceptor.cs similarity index 88% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerAsyncInterceptor.cs index 8ee8d2aa..3d7fba91 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerAsyncInterceptor.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerAsyncInterceptor.cs @@ -1,11 +1,11 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Consumer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IConsumerAsyncInterceptor : IInterceptorName { diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerInterceptor.cs similarity index 87% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerInterceptor.cs index 9cc73f37..2fbe60a2 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Interceptors/IConsumerInterceptor.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerInterceptor.cs @@ -1,11 +1,11 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Consumer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IConsumerInterceptor : IInterceptorName { diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerChannelProvider.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerChannelProvider.cs index 8297681f..768781e1 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerChannelProvider.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerChannelProvider.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Channel provider responsible for passing the actual message diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerStorageStrategy.cs similarity index 97% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerStorageStrategy.cs index 245568c6..041aba02 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Provider/IConsumerStorageStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerStorageStrategy.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible to load information from storage. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/ConsumerSegmentationStrategyBridge.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/ConsumerSegmentationStrategyBridge.cs index 9db25079..f1a210be 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/ConsumerSegmentationStrategyBridge.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/ConsumerSegmentationStrategyBridge.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Bridge segmentation diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerAsyncSegmenationStrategy.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerAsyncSegmenationStrategy.cs index 9db2a905..b68e99ab 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerAsyncSegmenationStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerAsyncSegmenationStrategy.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible of building instance from segmented data. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerSegmenationStrategy.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerSegmenationStrategy.cs index 738d6a2b..73ed226e 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/Segmentation/IConsumerSegmenationStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerSegmenationStrategy.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible of building instance from segmented data. diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs similarity index 97% rename from Consumers/EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs index c112c214..00a60ede 100644 --- a/Consumers/EventSource.Backbone.Consumers.Contracts/TelemetryrExtensions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs @@ -3,7 +3,7 @@ using OpenTelemetry; using OpenTelemetry.Context.Propagation; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class TelemetryrExtensions { diff --git a/Consumers/EventSource.Backbone.Consumers.Contracts/icon.png b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/icon.png similarity index 100% rename from Consumers/EventSource.Backbone.Consumers.Contracts/icon.png rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/icon.png diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index 22602c83..b0b83c34 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -2,12 +2,12 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.Enums; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; -using Handler = System.Func>; +using Handler = System.Func>; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.cs index 489bb1f0..118f87d4 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBase.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Base class for the consumer's code generator diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs similarity index 99% rename from Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs index 45b2972c..d3175d57 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs @@ -2,9 +2,9 @@ using System.Runtime.CompilerServices; using System.Text.Json; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public partial class ConsumerBuilder { diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs similarity index 98% rename from Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs index 823f755b..a06392b5 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs @@ -1,9 +1,9 @@ using System.Diagnostics; using System.Text.Json; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public partial class ConsumerBuilder { diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs similarity index 99% rename from Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index 599790d7..227baef0 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -7,11 +7,11 @@ using Polly; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.EventSourceConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source consumer builder. diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs similarity index 99% rename from Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index 8a1cfbe2..b6e93be9 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -5,10 +5,10 @@ using Polly; -using EventSource.Backbone.Building; -using EventSource.Backbone.Private; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Private; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Consumers/EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs similarity index 96% rename from Consumers/EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs rename to Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs index 3d87d912..ee45896f 100644 --- a/Consumers/EventSource.Backbone.Consumers/Builder/FilteredStorageStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Wrap Channel Storage with key filtering of the bucket. diff --git a/Consumers/EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs similarity index 95% rename from Consumers/EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs rename to Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs index 86215127..37cd751c 100644 --- a/Consumers/EventSource.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs @@ -1,9 +1,9 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible of converting raw segment (parameter) into object, i.e. deserialize a segment /// - /// + /// public class ConsumerDefaultSegmentationStrategy : IConsumerAsyncSegmentationStrategy { diff --git a/Consumers/EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj similarity index 62% rename from Consumers/EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj rename to Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj index 374fbf69..a7d0aeca 100644 --- a/Consumers/EventSource.Backbone.Consumers/EventSource.Backbone.Consumers.csproj +++ b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj @@ -1,9 +1,9 @@  - - - + + + diff --git a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj new file mode 100644 index 00000000..4fc36e51 --- /dev/null +++ b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Consumers/EventSource.Backbone.Consumers/icon.png b/Consumers/EventSourcing.Backbone.Consumers/icon.png similarity index 100% rename from Consumers/EventSource.Backbone.Consumers/icon.png rename to Consumers/EventSourcing.Backbone.Consumers/icon.png diff --git a/EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceGenType.cs similarity index 81% rename from EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs rename to EventSourcing.Backbone.Abstractions/Attributes/EventSourceGenType.cs index 7dbd9ce3..195970e5 100644 --- a/EventSource.Backbone.Contracts/Attributes/EventSourceGenType.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceGenType.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source Generation Type diff --git a/EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs similarity index 92% rename from EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs rename to EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs index 78e2d981..8bf11608 100644 --- a/EventSource.Backbone.Contracts/Attributes/EventSourceVersion.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event source's version control. @@ -8,7 +8,7 @@ /// We don't expect a gap between ConsumeFrom to a lower version. /// Versions expect to start at 0, if no version specified It will consider version 0, /// - /// + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class EventSourceVersionAttribute : Attribute { diff --git a/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs similarity index 82% rename from EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs rename to EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs index cce41ca7..e1b35910 100644 --- a/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceAttribute.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs @@ -1,9 +1,9 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Mark for code generation /// - /// + /// [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] public class GenerateEventSourceAttribute : GenerateEventSourceBaseAttribute { diff --git a/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs similarity index 97% rename from EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs rename to EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs index b26e9df9..c2ee44ca 100644 --- a/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBaseAttribute.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Mark for code generation diff --git a/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs similarity index 92% rename from EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs rename to EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs index aad1a55a..4c8aa0cc 100644 --- a/EventSource.Backbone.Contracts/Attributes/GenerateEventSourceBridgeAttribute.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { [Obsolete("Deprecated, use GenerateEventSourceAttribute instead", true)] [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] diff --git a/EventSource.Backbone.Contracts/Bucket.cs b/EventSourcing.Backbone.Abstractions/Bucket.cs similarity index 99% rename from EventSource.Backbone.Contracts/Bucket.cs rename to EventSourcing.Backbone.Abstractions/Bucket.cs index 53a3b0fe..78063933 100644 --- a/EventSource.Backbone.Contracts/Bucket.cs +++ b/EventSourcing.Backbone.Abstractions/Bucket.cs @@ -3,9 +3,9 @@ using System.Text; using System.Text.Json; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.EventSourceConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class Bucket : IEnumerable>> { diff --git a/EventSource.Backbone.Contracts/BucketJsonConverter.cs b/EventSourcing.Backbone.Abstractions/BucketJsonConverter.cs similarity index 96% rename from EventSource.Backbone.Contracts/BucketJsonConverter.cs rename to EventSourcing.Backbone.Abstractions/BucketJsonConverter.cs index 732a4913..e028f9da 100644 --- a/EventSource.Backbone.Contracts/BucketJsonConverter.cs +++ b/EventSourcing.Backbone.Abstractions/BucketJsonConverter.cs @@ -1,11 +1,11 @@ using System.Text.Json; using System.Text.Json.Serialization; -using static EventSource.Backbone.EventSourceOptions; +using static EventSourcing.Backbone.EventSourceOptions; using BucketData = System.Collections.Immutable.ImmutableDictionary>; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Announcement.cs similarity index 97% rename from EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs rename to EventSourcing.Backbone.Abstractions/Entities/Announcement/Announcement.cs index 60d31b0c..8fd100ac 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/Announcement.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Announcement.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Non-generics form of announcement representation, diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs similarity index 98% rename from EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs rename to EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs index cd78ee80..c7dfba6c 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/AnnouncementData.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Non-generics form of announcement representation, diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/EventSourceJsonContext.cs similarity index 88% rename from EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs rename to EventSourcing.Backbone.Abstractions/Entities/Announcement/EventSourceJsonContext.cs index dce218ad..0224b4e8 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/EventSourceJsonContext.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/EventSourceJsonContext.cs @@ -1,6 +1,6 @@ //using System.Text.Json.Serialization; -//using EventSource.Backbone; +//using EventSourcing.Backbone; ////[JsonSerializable(typeof(Announcement))] ////[JsonSerializable(typeof(AnnouncementData))] diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/MessageOrigin.cs similarity index 91% rename from EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs rename to EventSourcing.Backbone.Abstractions/Entities/Announcement/MessageOrigin.cs index 9b17bbdf..44ce1990 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/MessageOrigin.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/MessageOrigin.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// The message origin diff --git a/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs similarity index 99% rename from EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs rename to EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs index 14063330..fecb3b0f 100644 --- a/EventSource.Backbone.Contracts/Entities/Announcement/Metadata.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// /// Event Id diff --git a/EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs b/EventSourcing.Backbone.Abstractions/Entities/EventKey/EventKeys.cs similarity index 99% rename from EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs rename to EventSourcing.Backbone.Abstractions/Entities/EventKey/EventKeys.cs index deb2aed9..cf9ac276 100644 --- a/EventSource.Backbone.Contracts/Entities/EventKey/EventKeys.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/EventKey/EventKeys.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Immutable; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Id diff --git a/EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs b/EventSourcing.Backbone.Abstractions/Enums/EventBucketCategories.cs similarity index 86% rename from EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs rename to EventSourcing.Backbone.Abstractions/Enums/EventBucketCategories.cs index 523cd4ae..955fef8b 100644 --- a/EventSource.Backbone.Contracts/Enums/EventBucketCategories.cs +++ b/EventSourcing.Backbone.Abstractions/Enums/EventBucketCategories.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Bucket storage type diff --git a/EventSource.Backbone.Contracts/Env.cs b/EventSourcing.Backbone.Abstractions/Env.cs similarity index 98% rename from EventSource.Backbone.Contracts/Env.cs rename to EventSourcing.Backbone.Abstractions/Env.cs index 40d17630..c65180a3 100644 --- a/EventSource.Backbone.Contracts/Env.cs +++ b/EventSourcing.Backbone.Abstractions/Env.cs @@ -1,6 +1,6 @@ using static System.StringComparison; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Common Constants diff --git a/EventSource.Backbone.Contracts/EventSourceConstants.cs b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs similarity index 97% rename from EventSource.Backbone.Contracts/EventSourceConstants.cs rename to EventSourcing.Backbone.Abstractions/EventSourceConstants.cs index f687ae07..73d60bae 100644 --- a/EventSource.Backbone.Contracts/EventSourceConstants.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Constants diff --git a/EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs b/EventSourcing.Backbone.Abstractions/EventSourceFallbakLogger.cs similarity index 98% rename from EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs rename to EventSourcing.Backbone.Abstractions/EventSourceFallbakLogger.cs index e8275bca..b52a1c9e 100644 --- a/EventSource.Backbone.Contracts/EventSourceFallbakLogger.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceFallbakLogger.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging; -namespace EventSource.Backbone.Private +namespace EventSourcing.Backbone.Private { /// /// Best practice is to supply proper logger and diff --git a/EventSource.Backbone.Contracts/EventSourceOptions.cs b/EventSourcing.Backbone.Abstractions/EventSourceOptions.cs similarity index 98% rename from EventSource.Backbone.Contracts/EventSourceOptions.cs rename to EventSourcing.Backbone.Abstractions/EventSourceOptions.cs index dd96f4c3..c42b8aaf 100644 --- a/EventSource.Backbone.Contracts/EventSourceOptions.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceOptions.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public record EventSourceOptions { diff --git a/EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj similarity index 92% rename from EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj rename to EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj index 55b7b103..776e55d3 100644 --- a/EventSource.Backbone.Contracts/EventSource.Backbone.Contracts.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj @@ -1,7 +1,7 @@  - EventSource.Backbone + EventSourcing.Backbone diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj new file mode 100644 index 00000000..776e55d3 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -0,0 +1,19 @@ + + + + EventSourcing.Backbone + + + + + + + + + + + + + + + diff --git a/EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs b/EventSourcing.Backbone.Abstractions/Interfaces/IDataSerializer.cs similarity index 94% rename from EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs rename to EventSourcing.Backbone.Abstractions/Interfaces/IDataSerializer.cs index 06fae550..20632949 100644 --- a/EventSource.Backbone.Contracts/Interfaces/IDataSerializer.cs +++ b/EventSourcing.Backbone.Abstractions/Interfaces/IDataSerializer.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Enable to replace the default serialization diff --git a/EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs b/EventSourcing.Backbone.Abstractions/Interfaces/IInterceptorName.cs similarity index 89% rename from EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs rename to EventSourcing.Backbone.Abstractions/Interfaces/IInterceptorName.cs index e4ec6147..803b68ba 100644 --- a/EventSource.Backbone.Contracts/Interfaces/IInterceptorName.cs +++ b/EventSourcing.Backbone.Abstractions/Interfaces/IInterceptorName.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public interface IInterceptorName { diff --git a/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs b/EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs similarity index 92% rename from EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs rename to EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs index 0c86d830..cb310adf 100644 --- a/EventSource.Backbone.Contracts/Interfaces/IPlanRoute.cs +++ b/EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Plan routing identification diff --git a/EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs b/EventSourcing.Backbone.Abstractions/Interfaces/IWithCancellation.cs similarity index 91% rename from EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs rename to EventSourcing.Backbone.Abstractions/Interfaces/IWithCancellation.cs index 120552a4..50091cf9 100644 --- a/EventSource.Backbone.Contracts/Interfaces/IWithCancellation.cs +++ b/EventSourcing.Backbone.Abstractions/Interfaces/IWithCancellation.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source cancellation builder. diff --git a/EventSource.Backbone.Contracts/JsonDataSerializer.cs b/EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs similarity index 92% rename from EventSource.Backbone.Contracts/JsonDataSerializer.cs rename to EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs index f8eb83bd..9693e343 100644 --- a/EventSource.Backbone.Contracts/JsonDataSerializer.cs +++ b/EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs @@ -3,12 +3,12 @@ using static System.Text.Json.Extension.Constants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Json serializer (this is the default serializer) /// - /// + /// internal class JsonDataSerializer : IDataSerializer { private readonly JsonSerializerOptions _options; diff --git a/EventSource.Backbone.Contracts/TelemetryrExtensions.cs b/EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs similarity index 97% rename from EventSource.Backbone.Contracts/TelemetryrExtensions.cs rename to EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs index 4f9a96f4..c016c5e7 100644 --- a/EventSource.Backbone.Contracts/TelemetryrExtensions.cs +++ b/EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs @@ -2,7 +2,7 @@ using OpenTelemetry.Trace; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class TelemetryrExtensions { diff --git a/EventSource.Backbone.Contracts/icon.png b/EventSourcing.Backbone.Abstractions/icon.png similarity index 100% rename from EventSource.Backbone.Contracts/icon.png rename to EventSourcing.Backbone.Abstractions/icon.png diff --git a/EventSource.Backbone.sln b/EventSourcing.Backbone.sln similarity index 71% rename from EventSource.Backbone.sln rename to EventSourcing.Backbone.sln index 0707b2ef..bb9f0832 100644 --- a/EventSource.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -3,43 +3,43 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31825.309 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone", "EventSource.Backbone\EventSource.Backbone.csproj", "{52BFDB45-C61C-4FF8-98BA-41407FFC85F5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone", "EventSourcing.Backbone\EventSourcing.Backbone.csproj", "{52BFDB45-C61C-4FF8-98BA-41407FFC85F5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.UnitTests", "Tests\EventSource.Backbone.UnitTests\EventSource.Backbone.UnitTests.csproj", "{C1618305-4F0B-4AAA-8386-CA31DCBC5F59}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.UnitTests", "Tests\EventSourcing.Backbone.UnitTests\EventSourcing.Backbone.UnitTests.csproj", "{C1618305-4F0B-4AAA-8386-CA31DCBC5F59}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Contracts", "EventSource.Backbone.Contracts\EventSource.Backbone.Contracts.csproj", "{E17F4D78-8645-457E-B4CB-455FCED12F49}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Abstractions", "EventSourcing.Backbone.Abstractions\EventSourcing.Backbone.Abstractions.csproj", "{E17F4D78-8645-457E-B4CB-455FCED12F49}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Producers", "Producers", "{3FBE7FA1-700D-4B58-8569-15F533F761D1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Consumers", "Consumers", "{EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Producers.Contracts", "Producers\EventSource.Backbone.Producers.Contracts\EventSource.Backbone.Producers.Contracts.csproj", "{1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Producers.Abstractions", "Producers\EventSourcing.Backbone.Producers.Abstractions\EventSourcing.Backbone.Producers.Abstractions.csproj", "{1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Consumers.Contracts", "Consumers\EventSource.Backbone.Consumers.Contracts\EventSource.Backbone.Consumers.Contracts.csproj", "{8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Consumers.Abstractions", "Consumers\EventSourcing.Backbone.Consumers.Abstractions\EventSourcing.Backbone.Consumers.Abstractions.csproj", "{8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Consumers", "Consumers\EventSource.Backbone.Consumers\EventSource.Backbone.Consumers.csproj", "{6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Consumers", "Consumers\EventSourcing.Backbone.Consumers\EventSourcing.Backbone.Consumers.csproj", "{6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Producers", "Producers\EventSource.Backbone.Producers\EventSource.Backbone.Producers.csproj", "{90B4EBE9-7CDB-42E0-B5E2-9FCAC4AD52CA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Producers", "Producers\EventSourcing.Backbone.Producers\EventSourcing.Backbone.Producers.csproj", "{90B4EBE9-7CDB-42E0-B5E2-9FCAC4AD52CA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Channels", "Channels", "{75CE8A6F-D63B-491D-8834-8D3BA2127208}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Redis", "Redis", "{64FC6860-BD4C-4C11-BF6E-E99BB4D91723}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.IntegrationTests", "Tests\EventSource.Backbone.IntegrationTests\EventSource.Backbone.IntegrationTests.csproj", "{20A4298E-1929-4125-8D66-CABF330DE633}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.IntegrationTests", "Tests\EventSourcing.Backbone.IntegrationTests\EventSourcing.Backbone.IntegrationTests.csproj", "{20A4298E-1929-4125-8D66-CABF330DE633}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Channels.RedisProvider.Common", "Channels\REDIS\EventSource.Backbone.Channels.RedisProvider.Common\EventSource.Backbone.Channels.RedisProvider.Common.csproj", "{68387B20-DC7B-4C56-906E-9EC379677DE3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Channels.RedisProvider.Common", "Channels\REDIS\EventSourcing.Backbone.Channels.RedisProvider.Common\EventSourcing.Backbone.Channels.RedisProvider.Common.csproj", "{68387B20-DC7B-4C56-906E-9EC379677DE3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Channels.RedisProducerProvider", "Channels\REDIS\EventSource.Backbone.Channels.RedisProducerProvider\EventSource.Backbone.Channels.RedisProducerProvider.csproj", "{C4D21A30-B556-4272-85BD-8F7793BD7432}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Channels.RedisProducerProvider", "Channels\REDIS\EventSourcing.Backbone.Channels.RedisProducerProvider\EventSourcing.Backbone.Channels.RedisProducerProvider.csproj", "{C4D21A30-B556-4272-85BD-8F7793BD7432}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Channels.RedisConsumerProvider", "Channels\REDIS\EventSource.Backbone.Channels.RedisConsumerProvider\EventSource.Backbone.Channels.RedisConsumerProvider.csproj", "{ECC48028-F980-49E9-AAC1-65AF5AF3B16F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Channels.RedisConsumerProvider", "Channels\REDIS\EventSourcing.Backbone.Channels.RedisConsumerProvider\EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj", "{ECC48028-F980-49E9-AAC1-65AF5AF3B16F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Channels.S3StoreProvider.Common", "Channels\S3\EventSource.Backbone.Channels.S3StoreProvider.Common\EventSource.Backbone.Channels.S3StoreProvider.Common.csproj", "{54FA986A-47D9-4709-9C3E-AEB08CE73EBD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Channels.S3StoreProvider.Common", "Channels\S3\EventSourcing.Backbone.Channels.S3StoreProvider.Common\EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj", "{54FA986A-47D9-4709-9C3E-AEB08CE73EBD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Channels.S3StoreProducerProvider", "Channels\S3\EventSource.Backbone.Channels.S3StoreProducerProvider\EventSource.Backbone.Channels.S3StoreProducerProvider.csproj", "{686220F1-A72B-45FE-8A6C-148926973096}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Channels.S3StoreProducerProvider", "Channels\S3\EventSourcing.Backbone.Channels.S3StoreProducerProvider\EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj", "{686220F1-A72B-45FE-8A6C-148926973096}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.Channels.S3StoreConsumerProvider", "Channels\S3\EventSource.Backbone.Channels.S3StoreConsumerProvider\EventSource.Backbone.Channels.S3StoreConsumerProvider.csproj", "{39B1BBC2-1924-4B85-9BA4-3319F5C4F3E6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Channels.S3StoreConsumerProvider", "Channels\S3\EventSourcing.Backbone.Channels.S3StoreConsumerProvider\EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj", "{39B1BBC2-1924-4B85-9BA4-3319F5C4F3E6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6BD7580F-9EAE-4046-8EA9-10FBC8CF2C56}" ProjectSection(SolutionItems) = preProject @@ -49,13 +49,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "S3", "S3", "{1B19D36E-8348-4CAE-A2B8-87A81076539A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.WebEventTest", "Tests\EventSource.Backbone.WebEventTest\EventSource.Backbone.WebEventTest.csproj", "{D648BE46-4208-4DEB-9F2F-0B009B603D0A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.WebEventTest", "Tests\EventSourcing.Backbone.WebEventTest\EventSourcing.Backbone.WebEventTest.csproj", "{D648BE46-4208-4DEB-9F2F-0B009B603D0A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src-gen", "src-gen", "{2AE6684B-47A2-46D3-BE4E-C14B05255BB6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.SrcGen", "src-gen\EventSource.Backbone.SrcGen\EventSource.Backbone.SrcGen.csproj", "{230DA8DF-00EE-47E2-A7B8-54F05A8966F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.SrcGen", "src-gen\EventSourcing.Backbone.SrcGen\EventSourcing.Backbone.SrcGen.csproj", "{230DA8DF-00EE-47E2-A7B8-54F05A8966F6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSource.Backbone.SrcGen.Playground", "src-gen\EventSource.Backbone.SrcGen.Playground\EventSource.Backbone.SrcGen.Playground.csproj", "{49A8B718-DAB6-4D04-874C-0B1EA12826EB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.SrcGen.Playground", "src-gen\EventSourcing.Backbone.SrcGen.Playground\EventSourcing.Backbone.SrcGen.Playground.csproj", "{49A8B718-DAB6-4D04-874C-0B1EA12826EB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/EventSource.Backbone/EventSource.Backbone.xml b/EventSourcing.Backbone/EventSource.Backbone.xml similarity index 68% rename from EventSource.Backbone/EventSource.Backbone.xml rename to EventSourcing.Backbone/EventSource.Backbone.xml index f8f66565..1236099c 100644 --- a/EventSource.Backbone/EventSource.Backbone.xml +++ b/EventSourcing.Backbone/EventSource.Backbone.xml @@ -1,7 +1,7 @@ - EventSource.Backbone + EventSourcing.Backbone diff --git a/EventSource.Backbone/EventSource.Backbone.csproj b/EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj similarity index 77% rename from EventSource.Backbone/EventSource.Backbone.csproj rename to EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj index 67e081a0..b68940a8 100644 --- a/EventSource.Backbone/EventSource.Backbone.csproj +++ b/EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj @@ -7,7 +7,7 @@ - + diff --git a/EventSourcing.Backbone/EventSourcing.Backbone.csproj b/EventSourcing.Backbone/EventSourcing.Backbone.csproj new file mode 100644 index 00000000..f454acf1 --- /dev/null +++ b/EventSourcing.Backbone/EventSourcing.Backbone.csproj @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/EventSource.Backbone/icon.png b/EventSourcing.Backbone/icon.png similarity index 100% rename from EventSource.Backbone/icon.png rename to EventSourcing.Backbone/icon.png diff --git a/Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj b/Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj deleted file mode 100644 index 48c2cd0b..00000000 --- a/Producers/EventSource.Backbone.Producers/EventSource.Backbone.Producers.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerBuilderEnvironment.cs similarity index 90% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerBuilderEnvironment.cs index 856c812d..deaebb2e 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerBuilderEnvironment.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerBuilderEnvironment.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Origin environment of the message diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs similarity index 85% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs index 28167fda..d01616ac 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerEnvironmentBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Origin environment of the message diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerHooksBuilder.cs similarity index 99% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerHooksBuilder.cs index 3e1e063e..53558425 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerHooksBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerHooksBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Event Source producer builder. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerLoggerBuilder.cs similarity index 93% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerLoggerBuilder.cs index a3e07c31..cf7bfeaa 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerLoggerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerLoggerBuilder.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Event Source producer builder. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerOptionsBuilder.cs similarity index 89% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerOptionsBuilder.cs index 6ca7bc34..c8849f28 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerOptionsBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerOptionsBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Enable configuration. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerPartitionBuilder.cs similarity index 92% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerPartitionBuilder.cs index 747e7f47..f82cc728 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerPartitionBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerPartitionBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerRawBuilder.cs similarity index 90% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerRawBuilder.cs index b8181c92..51bc3992 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerRawBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerRawBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerSpecializeBuilder.cs similarity index 97% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerSpecializeBuilder.cs index a3016e79..dc96affa 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerSpecializeBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerSpecializeBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Event Source producer builder. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStorageStrategyWithFilter.cs similarity index 95% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStorageStrategyWithFilter.cs index e6e8aba4..8b5189bc 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStorageStrategyWithFilter.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStorageStrategyWithFilter.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible to save information to storage. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs similarity index 87% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs index 6f197e6e..0ffecf3d 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IProducerStoreStrategyBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs @@ -1,14 +1,14 @@  using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Enable configuration. /// - /// + /// public interface IProducerStoreStrategyBuilder : IProducerOptionsBuilder { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs similarity index 91% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs index b17f4708..78e054aa 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/IRawProducer.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source raw producer. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuildBuilder.cs similarity index 95% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuildBuilder.cs index 9c106713..3700f58c 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuildBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuildBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuilder.cs similarity index 91% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuilder.cs index 34da9544..5a339c94 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuilder.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs similarity index 93% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs index 0edb06ba..95e4d8f3 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverridePartitionBuilder.cs similarity index 94% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverridePartitionBuilder.cs index 00e7c167..57b41d37 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/Override/IProducerOverridePartitionBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverridePartitionBuilder.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Enable dynamic transformation of the stream id before sending. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RawProducerOptions.cs similarity index 90% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RawProducerOptions.cs index 43dc12b2..5b579b41 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RawProducerOptions.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RawProducerOptions.cs @@ -1,5 +1,5 @@  -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Raw producer options diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RouteAssignmentType.cs similarity index 83% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RouteAssignmentType.cs index 7a5cd001..7013c014 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/Building/RouteAssignmentType.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RouteAssignmentType.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// the type of the routing assignment diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs similarity index 93% rename from Producers/EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs index 7f078983..188c6bc5 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Builder/IProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source producer builder. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj similarity index 72% rename from Producers/EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj rename to Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj index c952ea69..35da22b8 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/EventSource.Backbone.Producers.Contracts.csproj +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj @@ -1,7 +1,7 @@  - + diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj new file mode 100644 index 00000000..7b7b7696 --- /dev/null +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerAsyncInterceptor.cs similarity index 89% rename from Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerAsyncInterceptor.cs index a2643ca3..8d30b340 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerAsyncInterceptor.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerAsyncInterceptor.cs @@ -1,11 +1,11 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Producer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IProducerAsyncInterceptor : IInterceptorName { diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerInterceptor.cs similarity index 86% rename from Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerInterceptor.cs index 3838e091..6601a219 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/IProducerInterceptor.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerInterceptor.cs @@ -1,11 +1,11 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Producer stage of an interception operation provider. /// It can be use for variety of responsibilities like /// flowing auth context or traces, producing metrics, etc. /// - /// + /// public interface IProducerInterceptor : IInterceptorName { diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/ProducerInterceptorBridge.cs similarity index 97% rename from Producers/EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/ProducerInterceptorBridge.cs index b9aa5006..397f5dc0 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Interceptors/ProducerInterceptorBridge.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/ProducerInterceptorBridge.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Bridge segmentation diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlan.cs similarity index 90% rename from Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlan.cs index c681d819..eed9805e 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlan.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlan.cs @@ -1,12 +1,12 @@ using System.Collections.Immutable; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Plan contract /// - /// + /// public interface IProducerPlan : IProducerPlanBase { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBase.cs similarity index 97% rename from Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBase.cs index 93041b44..f275b55c 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBase.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBase.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs similarity index 89% rename from Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs index cc6e5997..42b82b5d 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Plan/IProducerPlanBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs @@ -2,14 +2,14 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Plan builder contract /// - /// + /// public interface IProducerPlanBuilder : IProducerPlanBase { /// diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs similarity index 98% rename from Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs index be43718d..bae430ed 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Plan/ProducerPlan.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs @@ -2,10 +2,10 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.Private; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Private; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// @@ -397,7 +397,7 @@ public ProducerPlan AddForward(IProducerHooksBuilder forward) /// /// Not operational channel /// - /// + /// private class NopChannel : IProducerChannelProvider { public static readonly IProducerChannelProvider Empty = new NopChannel(); diff --git a/Producers/EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerExtensions.cs similarity index 95% rename from Producers/EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerExtensions.cs index 852347c2..2077b286 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/ProducerExtensions.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerExtensions.cs @@ -4,9 +4,9 @@ using Microsoft.Extensions.Logging; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.EventSourceConstants; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class ProducerExtensions { @@ -38,7 +38,7 @@ ValueTask Local(ILogger logger) /// /// Non strategy implementation /// - /// + /// private class VoidStorageStrategy : IProducerStorageStrategy { private readonly string _providerPrefix; diff --git a/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerPipeline.cs similarity index 99% rename from Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerPipeline.cs index 0c321ce5..95e53128 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/ProducerPipeline.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerPipeline.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Handle the producing pipeline diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerChannelProvider.cs similarity index 95% rename from Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerChannelProvider.cs index d24f484a..8846af05 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerChannelProvider.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerChannelProvider.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Channel provider responsible for passing the actual message diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerStorageStrategy.cs similarity index 97% rename from Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerStorageStrategy.cs index c1431b92..02207790 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Provider/IProducerStorageStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerStorageStrategy.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible to save information to storage. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerAsyncSegmentationStrategy.cs similarity index 98% rename from Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerAsyncSegmentationStrategy.cs index 1a36ff3e..36300dbe 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerAsyncSegmentationStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerAsyncSegmentationStrategy.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Responsible of splitting an instance into segments. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerSegmentationStrategy.cs similarity index 97% rename from Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerSegmentationStrategy.cs index c5700f0b..78b84686 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/IProducerSegmentationStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerSegmentationStrategy.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Responsible of splitting an instance into segments. diff --git a/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/ProducerSegmentationStrategyBridge.cs similarity index 98% rename from Producers/EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/ProducerSegmentationStrategyBridge.cs index cf258ddb..abf07786 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/Segmentation/ProducerSegmentationStrategyBridge.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/ProducerSegmentationStrategyBridge.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.Building +namespace EventSourcing.Backbone.Building { /// /// Bridge segmentation diff --git a/Producers/EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs similarity index 98% rename from Producers/EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs index 6674ab73..3337fd6f 100644 --- a/Producers/EventSource.Backbone.Producers.Contracts/TelemetryrExtensions.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs @@ -4,7 +4,7 @@ using OpenTelemetry; using OpenTelemetry.Context.Propagation; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class TelemetryrExtensions { diff --git a/Producers/EventSource.Backbone.Producers.Contracts/icon.png b/Producers/EventSourcing.Backbone.Producers.Abstractions/icon.png similarity index 100% rename from Producers/EventSource.Backbone.Producers.Contracts/icon.png rename to Producers/EventSourcing.Backbone.Producers.Abstractions/icon.png diff --git a/Producers/EventSource.Backbone.Producers/Builder/FilteredStorageStrategy.cs b/Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs similarity index 98% rename from Producers/EventSource.Backbone.Producers/Builder/FilteredStorageStrategy.cs rename to Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs index e0fdac68..62d096f7 100644 --- a/Producers/EventSource.Backbone.Producers/Builder/FilteredStorageStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Wrap Channel Storage with key filtering of the bucket. diff --git a/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs similarity index 99% rename from Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs rename to Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index 3428bf13..23803207 100644 --- a/Producers/EventSource.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -1,10 +1,10 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; using static System.String; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// Event Source producer builder. @@ -496,7 +496,7 @@ IProducerOverrideBuildBuilder IProducerOverridePartitionBuilder.Partition( /// /// Raw producer (useful for cluster migration) /// - /// + /// private class RawProducer : IRawProducer { private readonly IProducerPlan _plan; diff --git a/Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj b/Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj new file mode 100644 index 00000000..c23e0607 --- /dev/null +++ b/Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj b/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj new file mode 100644 index 00000000..87cd1b44 --- /dev/null +++ b/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Producers/EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs b/Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs similarity index 94% rename from Producers/EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs rename to Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs index b58a1abf..2be0d971 100644 --- a/Producers/EventSource.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class ProducerDefaultSegmentationStrategy : diff --git a/Producers/EventSource.Backbone.Producers/icon.png b/Producers/EventSourcing.Backbone.Producers/icon.png similarity index 100% rename from Producers/EventSource.Backbone.Producers/icon.png rename to Producers/EventSourcing.Backbone.Producers/icon.png diff --git a/Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs b/Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs deleted file mode 100644 index 293aa3d4..00000000 --- a/Tests/EventSource.Backbone.IntegrationTests/Entities/Person.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace EventSource.Backbone.UnitTests.Entities -{ - public record Person(int Id, string Name); - -} diff --git a/Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj b/Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj deleted file mode 100644 index 9f4a9bfe..00000000 --- a/Tests/EventSource.Backbone.IntegrationTests/EventSource.Backbone.IntegrationTests.csproj +++ /dev/null @@ -1,64 +0,0 @@ - - - - Debug;Release;Gen - false - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/EventSource.Backbone.WebEventTest/Dockerfile.develop b/Tests/EventSource.Backbone.WebEventTest/Dockerfile.develop deleted file mode 100644 index 2087ea49..00000000 --- a/Tests/EventSource.Backbone.WebEventTest/Dockerfile.develop +++ /dev/null @@ -1,18 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:5.0 -ARG BUILD_CONFIGURATION=Debug -ENV ASPNETCORE_ENVIRONMENT=Development -ENV ASPNETCORE_URLS=http://+:80 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true -EXPOSE 80 - -WORKDIR /src -COPY ["Tests/Weknow.EventSource.Backbone.WebEventTest/Weknow.EventSource.Backbone.WebEventTest.csproj", "Tests/Weknow.EventSource.Backbone.WebEventTest/"] - -RUN dotnet restore "Tests/Weknow.EventSource.Backbone.WebEventTest/Weknow.EventSource.Backbone.WebEventTest.csproj" -COPY . . -WORKDIR "/src/Tests/Weknow.EventSource.Backbone.WebEventTest" -RUN dotnet build --no-restore "Weknow.EventSource.Backbone.WebEventTest.csproj" -c $BUILD_CONFIGURATION - -RUN echo "exec dotnet run --no-build --no-launch-profile -c $BUILD_CONFIGURATION --" > /entrypoint.sh - -ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] \ No newline at end of file diff --git a/Tests/EventSource.Backbone.IntegrationTests/.editorconfig b/Tests/EventSourcing.Backbone.IntegrationTests/.editorconfig similarity index 100% rename from Tests/EventSource.Backbone.IntegrationTests/.editorconfig rename to Tests/EventSourcing.Backbone.IntegrationTests/.editorconfig diff --git a/Tests/EventSource.Backbone.IntegrationTests/Class1.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs similarity index 85% rename from Tests/EventSource.Backbone.IntegrationTests/Class1.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs index 7a935de3..184b269d 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Class1.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs @@ -3,13 +3,13 @@ using System.CodeDom.Compiler; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// /// Entity mapper is responsible of mapping announcement to DTO generated from SequenceOperationsConsumer /// /// - [GeneratedCode("EventSource.Backbone.SrcGen", "1.1.141.0")] + [GeneratedCode("EventSourcing.Backbone.SrcGen", "1.1.141.0")] public static class SequenceOperationsConsumerEntityMapperExtensions1 { /// diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs similarity index 93% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs index 49b64710..a30737d0 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlow.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs similarity index 87% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs index 66bafdc3..f917ad4c 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { [GenerateEventSource(EventSourceGenType.Consumer)] public interface IEventFlowStage1 diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs similarity index 90% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs index af129900..8f247a2b 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// /// use to test consumer which have partial operation from IEventFlow diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs similarity index 95% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs index 554cd273..cfae9399 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// /// The sequence operations. diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs similarity index 74% rename from src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs index a0fcc466..061abd0e 100644 --- a/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities; +namespace EventSourcing.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs similarity index 77% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs index 40bb29e6..dde32720 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities; +namespace EventSourcing.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs similarity index 75% rename from src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs index 81823de0..867874bd 100644 --- a/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities; +namespace EventSourcing.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs similarity index 94% rename from Tests/EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs index 3d0c6a1d..1cac0fad 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/DeleteKeysTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs @@ -6,9 +6,9 @@ using Xunit; using Xunit.Abstractions; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { public class DeleteKeysTests { diff --git a/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs similarity index 98% rename from Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 8802498d..ef5655a5 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -7,8 +7,8 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; @@ -19,7 +19,7 @@ // TODO: [bnaya 2020-10] ensure message order(cancel ack should cancel all following messages) // TODO: [bnaya 2020-10] check for no pending -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { /// /// The end to end explicit tests. diff --git a/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs similarity index 98% rename from Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 9640bf7b..5eae3bc0 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -7,17 +7,17 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { public class EndToEndStressTests : IDisposable { diff --git a/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs similarity index 99% rename from Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 004c8456..f4a2ed09 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -10,19 +10,19 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.Enums; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static EventSource.Backbone.EventSourceConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.EventSourceConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Entities/Person.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Entities/Person.cs new file mode 100644 index 00000000..94d9c95c --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Entities/Person.cs @@ -0,0 +1,5 @@ +namespace EventSourcing.Backbone.UnitTests.Entities +{ + public record Person(int Id, string Name); + +} diff --git a/Tests/EventSource.Backbone.IntegrationTests/Entities/User.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Entities/User.cs similarity index 94% rename from Tests/EventSource.Backbone.IntegrationTests/Entities/User.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/Entities/User.cs index 7381d49c..bc383200 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Entities/User.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Entities/User.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { public record User { diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj new file mode 100644 index 00000000..88566adc --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj @@ -0,0 +1,64 @@ + + + + Debug;Release;Gen + false + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj new file mode 100644 index 00000000..6effa167 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -0,0 +1,64 @@ + + + + Debug;Release;Gen + false + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs similarity index 93% rename from Tests/EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs index 82aeef47..c8be632f 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { public class InheritanceS3StoreStrategyTests : InheritanceTests { diff --git a/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs similarity index 98% rename from Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index ab25dcd3..d84c0eb5 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -10,17 +10,17 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.Enums; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; using Xunit; using Xunit.Abstractions; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs similarity index 98% rename from Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs index 599d2679..97ce9a7f 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -4,14 +4,14 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; using Xunit; using Xunit.Abstractions; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs similarity index 98% rename from Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index a6c24f13..bb923057 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -7,18 +7,18 @@ using StackExchange.Redis; -using EventSource.Backbone.Building; -using EventSource.Backbone.Enums; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -using static EventSource.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { /// /// The end to end tests. diff --git a/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs similarity index 89% rename from Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs index a6451cf9..4ea616ab 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { public class S3StoreStrategyExplicitTests : EndToEndExplicitTests { diff --git a/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs similarity index 93% rename from Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs rename to Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs index 5aa8f890..0025f602 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { public class S3StoreStrategyStressTests : EndToEndStressTests { diff --git a/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs similarity index 93% rename from Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs rename to Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs index df70df1a..6efda25a 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/S3StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs @@ -1,6 +1,6 @@ using Xunit.Abstractions; -namespace EventSource.Backbone.Tests +namespace EventSourcing.Backbone.Tests { public class S3StoreStrategyTests : EndToEndTests { diff --git a/Tests/EventSource.Backbone.IntegrationTests/icon.png b/Tests/EventSourcing.Backbone.IntegrationTests/icon.png similarity index 100% rename from Tests/EventSource.Backbone.IntegrationTests/icon.png rename to Tests/EventSourcing.Backbone.IntegrationTests/icon.png diff --git a/Tests/EventSource.Backbone.IntegrationTests/payload.json b/Tests/EventSourcing.Backbone.IntegrationTests/payload.json similarity index 100% rename from Tests/EventSource.Backbone.IntegrationTests/payload.json rename to Tests/EventSourcing.Backbone.IntegrationTests/payload.json diff --git a/Tests/EventSource.Backbone.IntegrationTests/person.json b/Tests/EventSourcing.Backbone.IntegrationTests/person.json similarity index 100% rename from Tests/EventSource.Backbone.IntegrationTests/person.json rename to Tests/EventSourcing.Backbone.IntegrationTests/person.json diff --git a/Tests/EventSource.Backbone.UnitTests/.editorconfig b/Tests/EventSourcing.Backbone.UnitTests/.editorconfig similarity index 100% rename from Tests/EventSource.Backbone.UnitTests/.editorconfig rename to Tests/EventSourcing.Backbone.UnitTests/.editorconfig diff --git a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs similarity index 95% rename from Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs index 190a3eda..89139d63 100644 --- a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs @@ -2,12 +2,12 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class EventSourceConsumerApiDesignTests { diff --git a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs similarity index 97% rename from Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs index 791e097b..05d5c762 100644 --- a/Tests/EventSource.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs @@ -2,15 +2,15 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class EventSourceApiProducerDesignTests { diff --git a/Tests/EventSource.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs similarity index 97% rename from Tests/EventSource.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs rename to Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs index a4195c8e..77a72582 100644 --- a/Tests/EventSource.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs @@ -4,9 +4,9 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class TestApiExtensions { diff --git a/Tests/EventSource.Backbone.UnitTests/Channels/ConsumerTestChannel.cs b/Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/Channels/ConsumerTestChannel.cs rename to Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs index 407bccd5..7a7db8e4 100644 --- a/Tests/EventSource.Backbone.UnitTests/Channels/ConsumerTestChannel.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs @@ -1,6 +1,6 @@ using System.Threading.Channels; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class ConsumerTestChannel : IConsumerChannelProvider diff --git a/Tests/EventSource.Backbone.UnitTests/Channels/ProducerTestChannel.cs b/Tests/EventSourcing.Backbone.UnitTests/Channels/ProducerTestChannel.cs similarity index 92% rename from Tests/EventSource.Backbone.UnitTests/Channels/ProducerTestChannel.cs rename to Tests/EventSourcing.Backbone.UnitTests/Channels/ProducerTestChannel.cs index ee663e80..fdcb4230 100644 --- a/Tests/EventSource.Backbone.UnitTests/Channels/ProducerTestChannel.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Channels/ProducerTestChannel.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using System.Threading.Channels; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// In-Memory Channel (excellent for testing) /// - /// + /// public class ProducerTestChannel : IProducerChannelProvider { diff --git a/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs similarity index 95% rename from Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs index 1d0ac1b9..0aa7f01f 100644 --- a/Tests/EventSource.Backbone.UnitTests/ConsumerBuilderTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs @@ -2,12 +2,12 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class ConsumerBuilderTests { diff --git a/Tests/EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs similarity index 93% rename from Tests/EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs rename to Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs index 71d8936d..e216039e 100644 --- a/Tests/EventSource.Backbone.UnitTests/Contracts/ISequenceOperations.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { [GenerateEventSource(EventSourceGenType.Producer, Name = "ISequenceOfProducer")] [GenerateEventSource(EventSourceGenType.Consumer, Name = "ISequenceOfConsumer")] diff --git a/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs similarity index 94% rename from Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs rename to Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs index ab81dac7..6d4d2d59 100644 --- a/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEvent.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// diff --git a/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs similarity index 89% rename from Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs rename to Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs index 3536128e..582c1901 100644 --- a/Tests/EventSource.Backbone.UnitTests/Contracts/ISimpleEventTag.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// /// Test contract diff --git a/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs index 3c9e93c4..a2621dfd 100644 --- a/Tests/EventSource.Backbone.UnitTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class EndToEndTests { diff --git a/Tests/EventSource.Backbone.UnitTests/Entities/User.cs b/Tests/EventSourcing.Backbone.UnitTests/Entities/User.cs similarity index 95% rename from Tests/EventSource.Backbone.UnitTests/Entities/User.cs rename to Tests/EventSourcing.Backbone.UnitTests/Entities/User.cs index c4a14a75..9350ddd9 100644 --- a/Tests/EventSource.Backbone.UnitTests/Entities/User.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Entities/User.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// /// The user. diff --git a/Tests/EventSource.Backbone.UnitTests/EventIdTests.cs b/Tests/EventSourcing.Backbone.UnitTests/EventIdTests.cs similarity index 97% rename from Tests/EventSource.Backbone.UnitTests/EventIdTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/EventIdTests.cs index 7b817333..85b6e6a0 100644 --- a/Tests/EventSource.Backbone.UnitTests/EventIdTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/EventIdTests.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class EventKeyTests { diff --git a/Tests/EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj similarity index 52% rename from Tests/EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj rename to Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj index e46b094c..9749fc79 100644 --- a/Tests/EventSource.Backbone.UnitTests/EventSource.Backbone.UnitTests.csproj +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj @@ -26,17 +26,17 @@ - - - - - - - + + + + + + + - + diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj new file mode 100644 index 00000000..0e17784d --- /dev/null +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj @@ -0,0 +1,46 @@ + + + + Debug;Release;Gen + false + disable + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs b/Tests/EventSourcing.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs rename to Tests/EventSourcing.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs index 08096990..32c8a97a 100644 --- a/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactory.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { public class SequenceOfProducerFactory : ProducerPipeline, ISequenceOfProducer diff --git a/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs b/Tests/EventSourcing.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs similarity index 85% rename from Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs rename to Tests/EventSourcing.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs index ea6e42c3..30fb840e 100644 --- a/Tests/EventSource.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Factory/ISequenceOfProducer/SequenceOfProducerFactoryExtensions.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { public static class SequenceOfProducerFactoryExtensions { diff --git a/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs similarity index 95% rename from Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs rename to Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs index db3d8319..855c22e8 100644 --- a/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks.Dataflow; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { public class SequenceOperationsConsumer : ISequenceOperationsConsumer { diff --git a/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs rename to Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs index 8f4c545a..02c738d9 100644 --- a/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactory.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { public class SequenceOperationsProducerFactory : ProducerPipeline, ISequenceOperationsProducer diff --git a/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs similarity index 79% rename from Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs rename to Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs index a65cc15b..47421154 100644 --- a/Tests/EventSource.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsProducerFactoryExtensions.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; -namespace EventSource.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { public static class SequenceOperationsProducerFactoryExtensions { diff --git a/Tests/EventSource.Backbone.UnitTests/InheritGenerationTest.cs b/Tests/EventSourcing.Backbone.UnitTests/InheritGenerationTest.cs similarity index 88% rename from Tests/EventSource.Backbone.UnitTests/InheritGenerationTest.cs rename to Tests/EventSourcing.Backbone.UnitTests/InheritGenerationTest.cs index be9a26d7..424ed1de 100644 --- a/Tests/EventSource.Backbone.UnitTests/InheritGenerationTest.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/InheritGenerationTest.cs @@ -1,11 +1,11 @@ -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class InheritGenerationTest { diff --git a/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs index b636bb50..ba6b23e6 100644 --- a/Tests/EventSource.Backbone.UnitTests/ProducerBuilderTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class ProducerBuilderTests { diff --git a/Tests/EventSource.Backbone.UnitTests/S3OptionsTests.cs b/Tests/EventSourcing.Backbone.UnitTests/S3OptionsTests.cs similarity index 99% rename from Tests/EventSource.Backbone.UnitTests/S3OptionsTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/S3OptionsTests.cs index 11a3faa2..19ddadf3 100644 --- a/Tests/EventSource.Backbone.UnitTests/S3OptionsTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/S3OptionsTests.cs @@ -2,7 +2,7 @@ -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class S3OptionsTests diff --git a/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs b/Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/SerializationTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs index c15b999a..c4dc8bf0 100644 --- a/Tests/EventSource.Backbone.UnitTests/SerializationTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class SerializationTests { diff --git a/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs b/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs index 51f63686..a9bfab79 100644 --- a/Tests/EventSource.Backbone.UnitTests/SourceMigrationTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs @@ -5,15 +5,15 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class SourceMigrationTests { diff --git a/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs similarity index 98% rename from Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs rename to Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs index 063c4d86..c6532439 100644 --- a/Tests/EventSource.Backbone.UnitTests/StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; using Xunit; using Xunit.Abstractions; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class StoreStrategyTests { diff --git a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs similarity index 79% rename from Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs rename to Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs index 7c9c1a45..92b7649f 100644 --- a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs @@ -1,12 +1,12 @@ -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// In-Memory Channel (excellent for testing) /// - /// + /// public class SimpleEventSubscription : SimpleEventSubscriptionBase { private readonly ISimpleEventConsumer _target; diff --git a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs similarity index 95% rename from Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs rename to Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs index b034f41f..facdcff8 100644 --- a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// diff --git a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs similarity index 91% rename from Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs rename to Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs index 3b9d839a..78ee946c 100644 --- a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs @@ -1,12 +1,12 @@ -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// In-Memory Channel (excellent for testing) /// - /// + /// public class SimpleEventSubscriptionBridge : ISubscriptionBridge { private readonly ISimpleEventConsumer _target; diff --git a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs similarity index 74% rename from Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs rename to Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs index 8ee0d667..f0a4d485 100644 --- a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridgeExtensions.cs @@ -1,7 +1,7 @@ -using EventSource.Backbone.Building; -using EventSource.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public static class SimpleEventSubscriptionBridgeExtensions { diff --git a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs similarity index 82% rename from Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs rename to Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs index 6709fcc8..8591c7cc 100644 --- a/Tests/EventSource.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs @@ -1,7 +1,7 @@ -using EventSource.Backbone.UnitTests.Entities; -using EventSource.Backbone.UnitTests.Entities.Hidden; +using EventSourcing.Backbone.UnitTests.Entities; +using EventSourcing.Backbone.UnitTests.Entities.Hidden; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { public class SimpleEventSubscriptionFromGen : SimpleEventConsumerBase diff --git a/Tests/EventSource.Backbone.UnitTests/icon.png b/Tests/EventSourcing.Backbone.UnitTests/icon.png similarity index 100% rename from Tests/EventSource.Backbone.UnitTests/icon.png rename to Tests/EventSourcing.Backbone.UnitTests/icon.png diff --git a/Tests/EventSource.Backbone.WebEventTest/.editorconfig b/Tests/EventSourcing.Backbone.WebEventTest/.editorconfig similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/.editorconfig rename to Tests/EventSourcing.Backbone.WebEventTest/.editorconfig diff --git a/Tests/EventSource.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs similarity index 99% rename from Tests/EventSource.Backbone.WebEventTest/AspCoreExtensions.cs rename to Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index 2946573e..eabddb0e 100644 --- a/Tests/EventSource.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -11,7 +11,7 @@ using StackExchange.Redis; -using EventSource.Backbone; +using EventSourcing.Backbone; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 diff --git a/Tests/EventSource.Backbone.WebEventTest/Contracts/IEventFlow.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs similarity index 88% rename from Tests/EventSource.Backbone.WebEventTest/Contracts/IEventFlow.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs index 13ae6082..28f228af 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Contracts/IEventFlow.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs @@ -1,12 +1,12 @@ using System.ComponentModel; using System.Text.Json; -namespace EventSource.Backbone.WebEventTest +namespace EventSourcing.Backbone.WebEventTest { [GenerateEventSource(EventSourceGenType.Consumer)] [GenerateEventSource(EventSourceGenType.Producer)] - //[GenerateEventSource(EventSourceGenType.Consumer, Namespace = "EventSource.Backbone.WebEventTest")] - //[GenerateEventSource(EventSourceGenType.Producer, Namespace = "EventSource.Backbone.WebEventTest")] + //[GenerateEventSource(EventSourceGenType.Consumer, Namespace = "EventSourcing.Backbone.WebEventTest")] + //[GenerateEventSource(EventSourceGenType.Producer, Namespace = "EventSourcing.Backbone.WebEventTest")] [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Used for code generation, use the producer / consumer version of it", true)] public interface IEventFlow diff --git a/Tests/EventSource.Backbone.WebEventTest/Contracts/IEventsMigration.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventsMigration.cs similarity index 91% rename from Tests/EventSource.Backbone.WebEventTest/Contracts/IEventsMigration.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventsMigration.cs index b33e08d7..b3a39091 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Contracts/IEventsMigration.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventsMigration.cs @@ -1,6 +1,6 @@ using Refit; -namespace EventSource.Backbone.WebEventTest +namespace EventSourcing.Backbone.WebEventTest { /// diff --git a/Tests/EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs similarity index 96% rename from Tests/EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs index a7284f4f..f80b9677 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Contracts/IVersionedEvents.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace EventSource.Backbone.WebEventTest +namespace EventSourcing.Backbone.WebEventTest { [GenerateEventSource(EventSourceGenType.Consumer)] [GenerateEventSource(EventSourceGenType.Producer)] diff --git a/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs similarity index 98% rename from Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs index 7cb8e1a0..f3678072 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Controllers/EventSourceApiController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs @@ -5,7 +5,7 @@ using StackExchange.Redis; -namespace EventSource.Backbone.WebEventTest.Controllers +namespace EventSourcing.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] [ApiController] diff --git a/Tests/EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/MigrationController.cs similarity index 94% rename from Tests/EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Controllers/MigrationController.cs index 2a84aca3..d99b138e 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Controllers/MigrationController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/MigrationController.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; -namespace EventSource.Backbone.WebEventTest.Controllers +namespace EventSourcing.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] [ApiController] diff --git a/Tests/EventSource.Backbone.WebEventTest/Controllers/TestController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs similarity index 97% rename from Tests/EventSource.Backbone.WebEventTest/Controllers/TestController.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs index 5812b788..0163acf5 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Controllers/TestController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs @@ -2,7 +2,7 @@ using StackExchange.Redis; -namespace EventSource.Backbone.WebEventTest.Controllers +namespace EventSourcing.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] [ApiController] diff --git a/Tests/EventSource.Backbone.WebEventTest/Dockerfile b/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/Dockerfile rename to Tests/EventSourcing.Backbone.WebEventTest/Dockerfile diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop b/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop new file mode 100644 index 00000000..d61e8ba9 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/sdk:5.0 +ARG BUILD_CONFIGURATION=Debug +ENV ASPNETCORE_ENVIRONMENT=Development +ENV ASPNETCORE_URLS=http://+:80 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true +EXPOSE 80 + +WORKDIR /src +COPY ["Tests/Weknow.EventSourcing.Backbone.WebEventTest/Weknow.EventSourcing.Backbone.WebEventTest.csproj", "Tests/Weknow.EventSourcing.Backbone.WebEventTest/"] + +RUN dotnet restore "Tests/Weknow.EventSourcing.Backbone.WebEventTest/Weknow.EventSourcing.Backbone.WebEventTest.csproj" +COPY . . +WORKDIR "/src/Tests/Weknow.EventSourcing.Backbone.WebEventTest" +RUN dotnet build --no-restore "Weknow.EventSourcing.Backbone.WebEventTest.csproj" -c $BUILD_CONFIGURATION + +RUN echo "exec dotnet run --no-build --no-launch-profile -c $BUILD_CONFIGURATION --" > /entrypoint.sh + +ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] \ No newline at end of file diff --git a/Tests/EventSource.Backbone.WebEventTest/Entities/Address.cs b/Tests/EventSourcing.Backbone.WebEventTest/Entities/Address.cs similarity index 60% rename from Tests/EventSource.Backbone.WebEventTest/Entities/Address.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Entities/Address.cs index 1a1b60c8..4507e0f7 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Entities/Address.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Entities/Address.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.WebEventTest +namespace EventSourcing.Backbone.WebEventTest { public record Address(string country, string city, string street); } diff --git a/Tests/EventSource.Backbone.WebEventTest/Entities/Person.cs b/Tests/EventSourcing.Backbone.WebEventTest/Entities/Person.cs similarity index 61% rename from Tests/EventSource.Backbone.WebEventTest/Entities/Person.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Entities/Person.cs index 7ff152ff..27625a48 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Entities/Person.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Entities/Person.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.WebEventTest +namespace EventSourcing.Backbone.WebEventTest { public record Person(int Id, string Name, Address? Address = null); diff --git a/Tests/EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj similarity index 56% rename from Tests/EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj rename to Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj index bdd72dd4..f5cb31a8 100644 --- a/Tests/EventSource.Backbone.WebEventTest/EventSource.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj @@ -24,16 +24,16 @@ - - - - - - + + + + + + - + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj new file mode 100644 index 00000000..f5cb31a8 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -0,0 +1,44 @@ + + + + Linux + ..\.. + Debug;Release;Gen + True + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/EventSource.Backbone.WebEventTest/Jobs/HostedServiceBase.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/HostedServiceBase.cs similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/Jobs/HostedServiceBase.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Jobs/HostedServiceBase.cs diff --git a/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs similarity index 97% rename from Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs index ed17f6c7..58128b30 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Jobs/MicroDemoJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs @@ -1,10 +1,10 @@ using System.Text.Json; -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; // TODO: Register the service at the Program.cs file services.AddHostedService<...> -namespace EventSource.Backbone.WebEventTest.Jobs +namespace EventSourcing.Backbone.WebEventTest.Jobs { /// /// MicroDemo Event Source Listener diff --git a/Tests/EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs similarity index 96% rename from Tests/EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs index 9bd93bf8..0b589877 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Jobs/MigrationJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs @@ -1,10 +1,10 @@ -using EventSource.Backbone.Building; +using EventSourcing.Backbone.Building; // TODO: Register the service at the Program.cs file services.AddHostedService<...> -namespace EventSource.Backbone.WebEventTest.Jobs +namespace EventSourcing.Backbone.WebEventTest.Jobs { /// /// MicroDemo Event Source Listener diff --git a/Tests/EventSource.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs similarity index 98% rename from Tests/EventSource.Backbone.WebEventTest/Program.cs rename to Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 278cf84c..a1f85389 100644 --- a/Tests/EventSource.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -6,8 +6,8 @@ using StackExchange.Redis; -using EventSource.Backbone.WebEventTest; -using EventSource.Backbone.WebEventTest.Jobs; +using EventSourcing.Backbone.WebEventTest; +using EventSourcing.Backbone.WebEventTest.Jobs; const string ENV = $"test"; diff --git a/Tests/EventSource.Backbone.WebEventTest/Properties/launchSettings.json b/Tests/EventSourcing.Backbone.WebEventTest/Properties/launchSettings.json similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/Properties/launchSettings.json rename to Tests/EventSourcing.Backbone.WebEventTest/Properties/launchSettings.json diff --git a/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs similarity index 97% rename from Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs rename to Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 71e51853..256a6e9a 100644 --- a/Tests/EventSource.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -1,6 +1,6 @@ -using EventSource.Backbone; +using EventSourcing.Backbone; -using EventSource.Backbone.WebEventTest; +using EventSourcing.Backbone.WebEventTest; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 diff --git a/Tests/EventSource.Backbone.WebEventTest/TestSampler.cs b/Tests/EventSourcing.Backbone.WebEventTest/TestSampler.cs similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/TestSampler.cs rename to Tests/EventSourcing.Backbone.WebEventTest/TestSampler.cs diff --git a/Tests/EventSource.Backbone.WebEventTest/appsettings.Development.json b/Tests/EventSourcing.Backbone.WebEventTest/appsettings.Development.json similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/appsettings.Development.json rename to Tests/EventSourcing.Backbone.WebEventTest/appsettings.Development.json diff --git a/Tests/EventSource.Backbone.WebEventTest/appsettings.json b/Tests/EventSourcing.Backbone.WebEventTest/appsettings.json similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/appsettings.json rename to Tests/EventSourcing.Backbone.WebEventTest/appsettings.json diff --git a/Tests/EventSource.Backbone.WebEventTest/azds.yaml b/Tests/EventSourcing.Backbone.WebEventTest/azds.yaml similarity index 95% rename from Tests/EventSource.Backbone.WebEventTest/azds.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/azds.yaml index 819af4bd..05947ad4 100644 --- a/Tests/EventSource.Backbone.WebEventTest/azds.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/azds.yaml @@ -46,6 +46,6 @@ configurations: - "!**/*.{sln,csproj}" command: [dotnet, run, --no-restore, --no-build, --no-launch-profile, -c, "${BUILD_CONFIGURATION:-Debug}"] iterate: - processesToKill: [dotnet, vsdbg, Weknow.EventSource.Backbone.WebEventTest] + processesToKill: [dotnet, vsdbg, Weknow.EventSourcing.Backbone.WebEventTest] buildCommands: - [dotnet, build, --no-restore, -c, "${BUILD_CONFIGURATION:-Debug}"] diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/.helmignore diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml rename to Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml diff --git a/Tests/EventSource.Backbone.WebEventTest/icon.png b/Tests/EventSourcing.Backbone.WebEventTest/icon.png similarity index 100% rename from Tests/EventSource.Backbone.WebEventTest/icon.png rename to Tests/EventSourcing.Backbone.WebEventTest/icon.png diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/EventSource.Backbone.SrcGen.Playground.csproj b/src-gen/EventSource.Backbone.SrcGen.Playground/EventSource.Backbone.SrcGen.Playground.csproj deleted file mode 100644 index a81099f3..00000000 --- a/src-gen/EventSource.Backbone.SrcGen.Playground/EventSource.Backbone.SrcGen.Playground.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - Debug;Release;Gen - True - - - - - - - - - - - - - - - - - - diff --git a/src-gen/EventSource.Backbone.SrcGen/Properties/launchSettings.json b/src-gen/EventSource.Backbone.SrcGen/Properties/launchSettings.json deleted file mode 100644 index 8a3a0d9f..00000000 --- a/src-gen/EventSource.Backbone.SrcGen/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "Weknow.EventSource.Backbone.SrcGen": { - "commandName": "DebugRoslynComponent", - "targetProject": "..\\Weknow.EventSource.Backbone.SrcGen.Playground\\Weknow.EventSource.Backbone.SrcGen.Playground.csproj" - } - } -} \ No newline at end of file diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj b/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj new file mode 100644 index 00000000..10aba54f --- /dev/null +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj @@ -0,0 +1,24 @@ + + + + Exe + Debug;Release;Gen + True + + + + + + + + + + + + + + + + + + diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/ISample.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs similarity index 89% rename from src-gen/EventSource.Backbone.SrcGen.Playground/ISample.cs rename to src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs index 6e67c905..5c6fb918 100644 --- a/src-gen/EventSource.Backbone.SrcGen.Playground/ISample.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.SrcGen.Playground +namespace EventSourcing.Backbone.SrcGen.Playground { /// /// Some doc diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs similarity index 74% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs rename to src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs index a0fcc466..061abd0e 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities; +namespace EventSourcing.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs similarity index 72% rename from src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs rename to src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs index deb6394a..a41ed8c0 100644 --- a/src-gen/EventSource.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities; +namespace EventSourcing.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs similarity index 75% rename from Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs rename to src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs index 81823de0..867874bd 100644 --- a/Tests/EventSource.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.UnitTests.Entities; +namespace EventSourcing.Backbone.UnitTests.Entities; [GenerateEventSource(EventSourceGenType.Producer)] [GenerateEventSource(EventSourceGenType.Consumer)] diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/Program.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Program.cs similarity index 74% rename from src-gen/EventSource.Backbone.SrcGen.Playground/Program.cs rename to src-gen/EventSourcing.Backbone.SrcGen.Playground/Program.cs index f1ae3e73..8db12a7d 100644 --- a/src-gen/EventSource.Backbone.SrcGen.Playground/Program.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Program.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.SrcGen.Playground +namespace EventSourcing.Backbone.SrcGen.Playground { internal class Program { diff --git a/src-gen/EventSource.Backbone.SrcGen.Playground/icon.png b/src-gen/EventSourcing.Backbone.SrcGen.Playground/icon.png similarity index 100% rename from src-gen/EventSource.Backbone.SrcGen.Playground/icon.png rename to src-gen/EventSourcing.Backbone.SrcGen.Playground/icon.png diff --git a/src-gen/EventSource.Backbone.SrcGen/EventSource.Backbone.SrcGen.csproj b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj similarity index 94% rename from src-gen/EventSource.Backbone.SrcGen/EventSource.Backbone.SrcGen.csproj rename to src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj index b6532b3e..22f2d3b8 100644 --- a/src-gen/EventSource.Backbone.SrcGen/EventSource.Backbone.SrcGen.csproj +++ b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj @@ -7,7 +7,7 @@ enable true false - Code generator for EventSource.Backbone + Code generator for EventSourcing.Backbone Debug;Release;Gen diff --git a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj new file mode 100644 index 00000000..22f2d3b8 --- /dev/null +++ b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj @@ -0,0 +1,38 @@ + + + + netstandard2.0 + true + 10 + enable + true + false + Code generator for EventSourcing.Backbone + Debug;Release;Gen + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + true + False + False + False + + + + + + + + + + + + diff --git a/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs similarity index 98% rename from src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 3b0c47b0..b3964ce3 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -4,10 +4,10 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using EventSource.Backbone.SrcGen.Generators.Entities; -using EventSource.Backbone.SrcGen.Generators.EntitiesAndHelpers; +using EventSourcing.Backbone.SrcGen.Generators.Entities; +using EventSourcing.Backbone.SrcGen.Generators.EntitiesAndHelpers; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { [Generator] internal class BridgeIncrementalGenerator : GeneratorIncrementalBase @@ -323,7 +323,7 @@ protected string OnGenerateProducer( string interfaceName) { var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; - builder.AppendLine("\tusing EventSource.Backbone.Building;"); + builder.AppendLine("\tusing EventSourcing.Backbone.Building;"); // CopyDocumentation(builder, kind, item, "\t"); string prefix = interfaceName.StartsWith("I") && diff --git a/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs similarity index 95% rename from src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index b67669ef..94a1cc0a 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -4,11 +4,11 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using EventSource.Backbone.SrcGen.Generators.Entities; +using EventSourcing.Backbone.SrcGen.Generators.Entities; -using static EventSource.Backbone.Helper; +using static EventSourcing.Backbone.Helper; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { [Generator] internal class ContractncrementalGenerator : GeneratorIncrementalBase diff --git a/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs similarity index 98% rename from src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs index 9264163b..de4a14bf 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs @@ -4,11 +4,11 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using EventSource.Backbone.SrcGen.Generators.Entities; +using EventSourcing.Backbone.SrcGen.Generators.Entities; -using static EventSource.Backbone.Helper; +using static EventSourcing.Backbone.Helper; -namespace EventSource.Backbone.SrcGen.Generators.EntitiesAndHelpers +namespace EventSourcing.Backbone.SrcGen.Generators.EntitiesAndHelpers { internal static class EntityGenerator { diff --git a/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs similarity index 92% rename from src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs index fbaea755..fb3852e5 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/GenInstruction.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.SrcGen.Generators.Entities +namespace EventSourcing.Backbone.SrcGen.Generators.Entities { internal class GenInstruction { diff --git a/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs similarity index 59% rename from src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs index cd4e80ef..8e1281c4 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/KindFilter.cs @@ -1,4 +1,4 @@ -namespace EventSource.Backbone.SrcGen.Generators.Entities +namespace EventSourcing.Backbone.SrcGen.Generators.Entities { internal enum KindFilter { diff --git a/src-gen/EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs similarity index 96% rename from src-gen/EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs index d6714d0d..36eb4c5c 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs @@ -5,11 +5,11 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using EventSource.Backbone.SrcGen.Generators.Entities; +using EventSourcing.Backbone.SrcGen.Generators.Entities; -using static EventSource.Backbone.Helper; +using static EventSourcing.Backbone.Helper; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { internal abstract class GeneratorIncrementalBase : IIncrementalGenerator @@ -22,8 +22,8 @@ internal abstract class GeneratorIncrementalBase : IIncrementalGenerator "System.Collections.Generic", "System.Threading.Tasks", "System.CodeDom.Compiler", - "EventSource.Backbone", - "EventSource.Backbone.Building" }.Select(u => $"using {u};").ToArray(); + "EventSourcing.Backbone", + "EventSourcing.Backbone.Building" }.Select(u => $"using {u};").ToArray(); private readonly ImmutableHashSet _targetAttribute = ImmutableHashSet.Empty; #region Ctor @@ -196,7 +196,7 @@ public void GenerateSingle( { builder.AppendLine(u); } - builder.AppendLine($"namespace {overrideNS ?? "EventSource.Backbone"}"); + builder.AppendLine($"namespace {overrideNS ?? "EventSourcing.Backbone"}"); builder.AppendLine("{"); builder.AppendLine(content); builder.AppendLine("}"); diff --git a/src-gen/EventSource.Backbone.SrcGen/Helper.cs b/src-gen/EventSourcing.Backbone.SrcGen/Helper.cs similarity index 98% rename from src-gen/EventSource.Backbone.SrcGen/Helper.cs rename to src-gen/EventSourcing.Backbone.SrcGen/Helper.cs index e2ed8c7a..fcadcf94 100644 --- a/src-gen/EventSource.Backbone.SrcGen/Helper.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Helper.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -namespace EventSource.Backbone +namespace EventSourcing.Backbone { /// /// diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json b/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json new file mode 100644 index 00000000..ae5d793d --- /dev/null +++ b/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Weknow.EventSourcing.Backbone.SrcGen": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\Weknow.EventSourcing.Backbone.SrcGen.Playground\\Weknow.EventSourcing.Backbone.SrcGen.Playground.csproj" + } + } +} \ No newline at end of file diff --git a/src-gen/EventSource.Backbone.SrcGen/RoslynHelper.cs b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs similarity index 100% rename from src-gen/EventSource.Backbone.SrcGen/RoslynHelper.cs rename to src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs diff --git a/src-gen/EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs b/src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs similarity index 99% rename from src-gen/EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs rename to src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs index 5ac601c1..21ab341d 100644 --- a/src-gen/EventSource.Backbone.SrcGen/SyntaxReceiverResult.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace EventSource.Backbone; +namespace EventSourcing.Backbone; ///// ///// Syntax Receiver Result Extensions diff --git a/src-gen/EventSource.Backbone.SrcGen/icon.png b/src-gen/EventSourcing.Backbone.SrcGen/icon.png similarity index 100% rename from src-gen/EventSource.Backbone.SrcGen/icon.png rename to src-gen/EventSourcing.Backbone.SrcGen/icon.png From c7bb62f05a89103e7abc4792d42f660c937069e7 Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 21 May 2023 10:06:02 +0000 Subject: [PATCH 006/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 08ebccde..e1c8102a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.18 + 1.2.19 From 43986187516f90588654c088e80cac8dc393e9a4 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 21 May 2023 10:06:17 +0000 Subject: [PATCH 007/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index e1c8102a..96d7f484 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.19 + 1.2.20 @@ -44,7 +44,7 @@ - + From 5cbef796c378dfc014973a77cb1033ba99d6c615 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 21 May 2023 13:19:53 +0300 Subject: [PATCH 008/178] rfc: remove warnings --- .../RedisConsumerChannel.cs | 8 +- .../RedisConsumerProviderExtensions.cs | 8 +- .../RedisProviderExtensions.cs | 8 +- .../RedisDiExtensions.cs | 4 +- .../S3ConsumerStorageStrategy.cs | 4 +- .../S3ConsumerStorageStrategyExtension.cs | 4 +- .../S3ProducerStorageStrategy.cs | 4 +- .../S3ProducerStorageStrategyExtension.cs | 4 +- .../Builder/Building/IConsumerReadyBuilder.cs | 6 +- .../Builder/IConsumerBuilder.cs | 4 +- .../Builder/IConsumerPlanBuilder.cs | 4 +- .../ConsumerBase.EventSourceSubscriber.cs | 4 +- .../Builder/ConsumerBuilder.cs | 4 +- .../Builder/ConsumerPlan.cs | 6 +- EventSourcing.Backbone.Abstractions/Bucket.cs | 8 +- .../Entities/Announcement/Metadata.cs | 4 +- .../Building/IProducerStoreStrategyBuilder.cs | 4 +- .../Builder/IProducerBuilder.cs | 4 +- .../Plan/IProducerPlanBuilder.cs | 4 +- .../Plan/ProducerPlan.cs | 33 ++---- .../Builder/ProducerBuilder.cs | 4 +- .../EndToEndExplicitTests.cs | 6 +- .../EndToEndStressTests.cs | 6 +- .../EndToEndTests.cs | 8 +- .../InheritanceTests.cs | 38 +++---- .../MigrationReceiverTest.cs | 102 +----------------- .../MigrationTest.cs | 8 +- .../EventSourceApiConsumerDesignTests.cs | 4 +- .../EventSourceApiProducerDesignTests.cs | 6 +- .../ApiDesign/TestApiExtensions.cs | 4 +- .../ConsumerBuilderTests.cs | 4 +- .../EndToEndTests.cs | 6 +- .../ProducerBuilderTests.cs | 34 ++---- .../SourceMigrationTests.cs | 6 +- .../StoreStrategyTests.cs | 6 +- .../AspCoreExtensions.cs | 6 +- .../Jobs/MicroDemoJob.cs | 10 +- .../Program.cs | 6 +- .../Concrete/BridgeIncrementalGenerator.cs | 6 +- .../Concrete/ContractIncrementalGenerator.cs | 4 +- .../EntitiesAndHelpers/EntityGenerator.cs | 4 +- .../Generators/GeneratorIncrementalBase.cs | 4 +- 42 files changed, 141 insertions(+), 270 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 2f53e5cc..63c4b720 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -3,14 +3,14 @@ using System.Runtime.CompilerServices; using System.Text.Json; -using Microsoft.Extensions.Logging; - -using StackExchange.Redis; - using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider.Common; using EventSourcing.Backbone.Private; +using Microsoft.Extensions.Logging; + +using StackExchange.Redis; + using static System.Math; using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs index 83857532..cc46c490 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs @@ -1,12 +1,12 @@ -using Microsoft.Extensions.Configuration; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; + +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StackExchange.Redis; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Channels.RedisProvider; - namespace EventSourcing.Backbone { public static class RedisConsumerProviderExtensions diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs index 12275647..a71fab88 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.Configuration; +using EventSourcing.Backbone.Channels.RedisProvider; +using EventSourcing.Backbone.Private; + +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -8,9 +11,6 @@ using StackExchange.Redis; -using EventSourcing.Backbone.Channels.RedisProvider; -using EventSourcing.Backbone.Private; - namespace EventSourcing.Backbone { public static class RedisProviderExtensions diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs index eeac12be..6d386c01 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs @@ -1,6 +1,6 @@ -using Microsoft.Extensions.DependencyInjection; +using EventSourcing.Backbone; -using EventSourcing.Backbone; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Configuration { diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs index 8082fedc..4a832bed 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs @@ -1,10 +1,10 @@ using System.Collections.Immutable; using System.Text.Json; -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Channels; +using Microsoft.Extensions.Logging; + using static EventSourcing.Backbone.EventSourceConstants; diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 5c56a67f..5163b7d2 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -1,9 +1,9 @@  -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels; +using Microsoft.Extensions.Logging; + namespace EventSourcing.Backbone { diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs index f5f86ab6..4f300965 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs @@ -1,10 +1,10 @@ using System.Collections.Immutable; using System.Text.Json; -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Channels; +using Microsoft.Extensions.Logging; + using static EventSourcing.Backbone.EventSourceConstants; diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index 7c0ff25c..bf503ed1 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -1,8 +1,8 @@  -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Channels; +using Microsoft.Extensions.Logging; + namespace EventSourcing.Backbone { diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs index 2c04f91c..aeed017b 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs @@ -1,8 +1,8 @@ -using Microsoft.Extensions.Logging; +using EventSourcing.Backbone.Building; -using Polly; +using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; +using Polly; namespace EventSourcing.Backbone { diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs index f2dfb1df..43cc9621 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs @@ -1,6 +1,6 @@ -using Microsoft.Extensions.Logging; +using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; namespace EventSourcing.Backbone { diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs index b9c73b1e..b77c898d 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs @@ -1,9 +1,9 @@ using System.Collections.Immutable; -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; + namespace EventSourcing.Backbone { /// diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index b0b83c34..f39e8f83 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -1,10 +1,10 @@ using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Enums; +using Microsoft.Extensions.Logging; + using Handler = System.Func>; namespace EventSourcing.Backbone diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index 227baef0..73a81149 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -3,12 +3,12 @@ using System.Text; using System.Text.Json; +using EventSourcing.Backbone.Building; + using Microsoft.Extensions.Logging; using Polly; -using EventSourcing.Backbone.Building; - using static EventSourcing.Backbone.EventSourceConstants; namespace EventSourcing.Backbone diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index b6e93be9..3a2eba6a 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using System.Diagnostics; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Private; + using Microsoft.Extensions.Logging; using Polly; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Private; - namespace EventSourcing.Backbone { diff --git a/EventSourcing.Backbone.Abstractions/Bucket.cs b/EventSourcing.Backbone.Abstractions/Bucket.cs index 78063933..263266e5 100644 --- a/EventSourcing.Backbone.Abstractions/Bucket.cs +++ b/EventSourcing.Backbone.Abstractions/Bucket.cs @@ -186,13 +186,13 @@ public bool ContainsKey(string key) /// public bool TryGetValue(string key, out ReadOnlyMemory value) { - if(_data.TryGetValue(key, out value)) + if (_data.TryGetValue(key, out value)) return true; - if(_data.TryGetValue(key.ToPascal(), out value)) + if (_data.TryGetValue(key.ToPascal(), out value)) return true; - if(_data.TryGetValue(key.ToCamel(), out value)) + if (_data.TryGetValue(key.ToCamel(), out value)) return true; - if(_data.TryGetValue(key.ToDash(), out value)) + if (_data.TryGetValue(key.ToDash(), out value)) return true; return _data.TryGetValue(key.ToLower(), out value); } diff --git a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs index fecb3b0f..7d6ab9ef 100644 --- a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs @@ -50,7 +50,7 @@ public record Metadata /// /// Gets or sets the operation. /// - public required string Operation { get; init; } + public required string Operation { get; init; } #endregion Operation @@ -119,7 +119,7 @@ public static class MetadataExtensions { private const string EMPTY_KEY = "~EMPTY~"; - public static readonly Metadata Empty = new Metadata { MessageId = EMPTY_KEY, ChannelType = "NONE", EventKey = string.Empty, Operation="NONE", Uri=string.Empty }; + public static readonly Metadata Empty = new Metadata { MessageId = EMPTY_KEY, ChannelType = "NONE", EventKey = string.Empty, Operation = "NONE", Uri = string.Empty }; #region Duration diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs index 0ffecf3d..d6bf644b 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs @@ -1,8 +1,8 @@  -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; + namespace EventSourcing.Backbone { /// diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs index 188c6bc5..74423cbd 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs @@ -1,6 +1,6 @@ -using Microsoft.Extensions.Logging; +using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; namespace EventSourcing.Backbone { diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs index 42b82b5d..2d4f5478 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs @@ -1,9 +1,9 @@ using System.Collections.Immutable; -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; + namespace EventSourcing.Backbone { /// diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs index bae430ed..be9e269d 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs @@ -1,10 +1,10 @@ using System.Collections.Immutable; -using Microsoft.Extensions.Logging; - using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Private; +using Microsoft.Extensions.Logging; + namespace EventSourcing.Backbone { @@ -34,7 +34,6 @@ private ProducerPlan() /// The channel. /// The environment. /// The partition. - /// The shard. /// The logger. /// The options. /// The segmentation strategies. @@ -96,8 +95,13 @@ IProducerChannelProvider IProducerPlan.Channel { get { +#pragma warning disable S2372 // Exceptions should not be thrown from property getters +#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one if (_channel == null) throw new ArgumentNullException("Event Source Producer channel not set"); +#pragma warning restore S3928 // +#pragma warning restore S2372 + return _channel; } } @@ -392,29 +396,6 @@ public ProducerPlan AddForward(IProducerHooksBuilder forward) #endregion // AddForward - #region class NopChannel - - /// - /// Not operational channel - /// - /// - private class NopChannel : IProducerChannelProvider - { - public static readonly IProducerChannelProvider Empty = new NopChannel(); - /// - /// Send. - /// - /// The payload. - /// The storage strategy. - /// A ValueTask. - public ValueTask SendAsync(Announcement payload, ImmutableArray storageStrategy) - { - throw new NotSupportedException("Channel must be assign"); - } - } - - #endregion // class NopChannel - // --------------------------------------- #region Build diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index 23803207..d856aef2 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -1,6 +1,6 @@ -using Microsoft.Extensions.Logging; +using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; using static System.String; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index ef5655a5..5faffe7e 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -1,15 +1,15 @@ using System.Diagnostics; using System.Text.Json; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; using StackExchange.Redis; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 5eae3bc0..cf6e6934 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -1,15 +1,15 @@ using System.Diagnostics; using System.Threading.Tasks.Dataflow; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; using StackExchange.Redis; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index f4a2ed09..82ede346 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -2,6 +2,10 @@ using System.Text.Json; using System.Threading.Tasks.Dataflow; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; @@ -10,10 +14,6 @@ using StackExchange.Redis; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Enums; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index d84c0eb5..a27bf361 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -4,15 +4,15 @@ using System.Diagnostics; using System.Threading.Tasks.Dataflow; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; + using FakeItEasy; using Microsoft.Extensions.Logging; using StackExchange.Redis; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Enums; - using Xunit; using Xunit.Abstractions; @@ -205,38 +205,38 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() var hash = new ConcurrentDictionary(); A.CallTo(() => _subscriberA.AAsync(1)) - .Invokes(c => - { - hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); - if(Interlocked.Increment(ref i) == 3) + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); A.CallTo(() => _subscriberB.BAsync(A.Ignored)) - .Invokes(c => + .Invokes(c => { - hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); - if(Interlocked.Increment(ref i) == 3) + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); A.CallTo(() => _subscriberAB.DerivedAsync("Hi")) - .Invokes(c => - { - hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); - if(Interlocked.Increment(ref i) == 3) + .Invokes(c => + { + hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); + if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); A.CallTo(() => _subscriberAB.AAsync(1)) - .Invokes(c => - { + .Invokes(c => + { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); - if(Interlocked.Increment(ref i) == 3) + if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); A.CallTo(() => _subscriberAB.BAsync(A.Ignored)) - .Invokes(c => + .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); - if(Interlocked.Increment(ref i) == 3) + if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs index 97ce9a7f..64cbb290 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -1,11 +1,11 @@ using System.Diagnostics; +using EventSourcing.Backbone.Building; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; - using Xunit; using Xunit.Abstractions; @@ -25,11 +25,7 @@ public class MigrationReceiverTest // : IDisposable private readonly IProducerStoreStrategyBuilder _targetProducerBuilder; private readonly IConsumerStoreStrategyBuilder _sourceConsumerBuilder; private readonly string ENV = "Production"; - //private readonly string ENV = "Development"; - private readonly string PARTITION = "analysts"; - //private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly string SHARD = "default"; - + private readonly string URI = "analysts"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1_000 * 300; @@ -44,7 +40,6 @@ public MigrationReceiverTest(ITestOutputHelper outputHelper) _outputHelper = outputHelper; _targetProducerBuilder = ProducerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsKeys { EndpointKey = TARGET_KEY }) .AddVoidStrategy(); - //.AddS3Strategy(new S3Options { Bucket="temp"}); _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsKeys { EndpointKey = SOURCE_KEY }) .AddS3Strategy(new S3Options { Bucket = "event-source-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); @@ -63,7 +58,7 @@ public async Task Migration_By_Receiver_Test() IRawProducer rawProducer = _targetProducerBuilder .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .WithLogger(_fakeLogger) .BuildRaw(new RawProducerOptions { KeepOriginalMeta = true }); @@ -75,7 +70,7 @@ public async Task Migration_By_Receiver_Test() IAsyncEnumerable announcements = _sourceConsumerBuilder .WithCancellation(cancellation) .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .WithLogger(_fakeLogger) .BuildIterator() .GetAsyncEnumerable(new ConsumerAsyncEnumerableOptions { ExitWhenEmpty = true }); @@ -107,92 +102,5 @@ private static CancellationToken GetCancellationToken() } #endregion // GetCancellationToken - - #region // Dispose pattern - - - //~MigrationReceiverTest() - //{ - // Dispose(); - //} - - //public void Dispose() - //{ - // GC.SuppressFinalize(this); - // try - // { - // IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - // cfg => cfg.AllowAdmin = true); - // string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - // var server = conn.GetServer(serverName); - // IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); - // IDatabaseAsync db = conn.GetDatabase(); - - // var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); - // foreach (string key in keys) - // { - // ab.Post(key); - // } - - // ab.Complete(); - // ab.Completion.Wait(); - - // async Task LocalAsync(string k) - // { - // try - // { - // await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - // _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - // } - // #region Exception Handling - - // catch (RedisTimeoutException ex) - // { - // _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - // } - // catch (Exception ex) - // { - // _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - // } - - // #endregion // Exception Handling - // } - // } - // #region Exception Handling - - // catch (RedisTimeoutException ex) - // { - // _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - // } - // catch (Exception ex) - // { - // _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); - // } - - // #endregion // Exception Handling - //} - - #endregion // Dispose pattern - - #region SubscriptionBridge - - private class SubscriptionBridge : ISubscriptionBridge - { - private readonly IRawProducer _fw; - - public SubscriptionBridge(IRawProducer fw) - { - _fw = fw; - } - - public async Task BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) - { - await _fw.Produce(announcement); - return true; - - } - } - - #endregion // SubscriptionBridge } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index bb923057..88c95741 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -1,16 +1,16 @@ using System.Diagnostics; using System.Threading.Tasks.Dataflow; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; using StackExchange.Redis; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Enums; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs index 89139d63..58e37df2 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiConsumerDesignTests.cs @@ -1,9 +1,9 @@ +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs index 05d5c762..0df0e206 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/EventSourceApiProducerDesignTests.cs @@ -1,10 +1,10 @@ +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs index 77a72582..3778a245 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ApiDesign/TestApiExtensions.cs @@ -1,11 +1,11 @@ using System.Collections.Immutable; +using EventSourcing.Backbone.Building; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; - namespace EventSourcing.Backbone { public static class TestApiExtensions diff --git a/Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs index 0aa7f01f..fba7afea 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ConsumerBuilderTests.cs @@ -1,9 +1,9 @@ +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs index a2621dfd..35400014 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs @@ -1,12 +1,12 @@ using System.Threading.Channels; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs b/Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs index ba6b23e6..285f2aa2 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/ProducerBuilderTests.cs @@ -1,15 +1,18 @@ using System.Threading.Channels; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; +#pragma warning disable S2699 // Tests should include assertions +#pragma warning disable S1481 // Unused local variables should be removed +#pragma warning disable S125 // Sections of code should not be commented out namespace EventSourcing.Backbone @@ -19,7 +22,6 @@ public class ProducerBuilderTests private readonly ITestOutputHelper _outputHelper; private readonly IProducerBuilder _builder = ProducerBuilder.Empty; private readonly Func _channel; - //private readonly IDataSerializer _serializer; private readonly IProducerInterceptor _rawInterceptor = A.Fake(); private readonly IProducerAsyncInterceptor _rawAsyncInterceptor = A.Fake(); private readonly IProducerAsyncSegmentationStrategy _segmentationStrategy = A.Fake(); @@ -70,6 +72,8 @@ public async Task Build_Merge_Producer_Test() .UseSegmentation(_postSegmentationStrategy) .CustomBuildSequenceOfProducer(); + _outputHelper.WriteLine("sending"); + string[] ids1 = await producer.RegisterAsync(new User()); string[] ids2 = await producer.LoginAsync("admin", "1234"); string[] ids3 = await producer.EarseAsync(4335); @@ -149,28 +153,6 @@ public async Task Build_GeneratedFactory_Specialize_Producer_Test() #endregion // Build_GeneratedFactory_Specialize_Producer_Test - #region Build_Factory_Producer_Test - - [Fact] - public async Task Build_Factory_Producer_Test() - { - //var option = new EventSourceOptions(_serializer); - - ISequenceOperationsProducer producer = - _builder.UseChannel(_channel) - //.WithOptions(option) - .Uri("Kids:HappySocks") - .BuildSequenceOperationsProducer(); - - await producer.RegisterAsync(new User()); - await producer.LoginAsync("admin", "1234"); - await producer.EarseAsync(4335); - - var message = await ch.Reader.ReadAsync(); - } - - #endregion // Build_Factory_Producer_Test - #region Build_Factory_Producer_WithReturn_Test [Fact] diff --git a/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs b/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs index a9bfab79..5fcacb1e 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs @@ -1,13 +1,13 @@ using System.Threading.Channels; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs index c6532439..ce1ef435 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs @@ -1,12 +1,12 @@ using System.Threading.Channels; +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.UnitTests.Entities; - using Xunit; using Xunit.Abstractions; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index eabddb0e..f02b2cab 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -1,6 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +using EventSourcing.Backbone; + using Microsoft.AspNetCore.Server.Kestrel.Core; using Newtonsoft.Json.Serialization; @@ -11,8 +13,6 @@ using StackExchange.Redis; -using EventSourcing.Backbone; - // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 @@ -94,7 +94,7 @@ public static IServiceCollection AddOpenTelemetryWeknow( // ) .AddOtlpExporter() .SetSampler(TestSampler.Create(LogLevel.Information)); - + if (hostEnv.IsDevelopment()) { builder.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Console); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs index 58128b30..641178de 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs @@ -2,8 +2,6 @@ using EventSourcing.Backbone.Building; -// TODO: Register the service at the Program.cs file services.AddHostedService<...> - namespace EventSourcing.Backbone.WebEventTest.Jobs { /// @@ -59,13 +57,15 @@ protected override async Task OnStartAsync(CancellationToken cancellationToken) /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// +#pragma warning disable S1186 // Methods should not be empty public void Dispose() { } +#pragma warning restore S1186 // Methods should not be empty #endregion Dispose - private class Subscriber : IEventFlowConsumer + private sealed class Subscriber : IEventFlowConsumer { private readonly ILogger _logger; private readonly IEventFlowProducer _producer; @@ -83,7 +83,7 @@ async ValueTask IEventFlowConsumer.Stage1Async(Person PII, string payload) { Metadata? meta = ConsumerMetadata.Context; - _logger.LogInformation("Consume First Stage {partition} {shard} {PII} {data}", + _logger.LogInformation("Consume First Stage {partition} {PII} {data}", meta?.Uri, PII, payload); await _producer.Stage2Async( @@ -94,7 +94,7 @@ await _producer.Stage2Async( ValueTask IEventFlowConsumer.Stage2Async(JsonElement PII, JsonElement data) { var meta = ConsumerMetadata.Context; - _logger.LogInformation("Consume 2 Stage {partition} {shard} {PII} {data}", + _logger.LogInformation("Consume 2 Stage {partition} {PII} {data}", meta?.Metadata?.Uri, PII, data); return ValueTask.CompletedTask; } diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index a1f85389..b5873d71 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -1,14 +1,14 @@ using System.Text.Json; +using EventSourcing.Backbone.WebEventTest; +using EventSourcing.Backbone.WebEventTest.Jobs; + using Microsoft.OpenApi.Models; using Refit; using StackExchange.Redis; -using EventSourcing.Backbone.WebEventTest; -using EventSourcing.Backbone.WebEventTest.Jobs; - const string ENV = $"test"; var builder = WebApplication.CreateBuilder(args); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index b3964ce3..57490e84 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -1,12 +1,12 @@ using System.Reflection; using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; - using EventSourcing.Backbone.SrcGen.Generators.Entities; using EventSourcing.Backbone.SrcGen.Generators.EntitiesAndHelpers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + namespace EventSourcing.Backbone { [Generator] diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 94a1cc0a..72e37bc8 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -1,11 +1,11 @@ using System.Text; +using EventSourcing.Backbone.SrcGen.Generators.Entities; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using EventSourcing.Backbone.SrcGen.Generators.Entities; - using static EventSourcing.Backbone.Helper; namespace EventSourcing.Backbone diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs index de4a14bf..56c526aa 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs @@ -1,11 +1,11 @@ using System.Reflection; using System.Text; +using EventSourcing.Backbone.SrcGen.Generators.Entities; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using EventSourcing.Backbone.SrcGen.Generators.Entities; - using static EventSourcing.Backbone.Helper; namespace EventSourcing.Backbone.SrcGen.Generators.EntitiesAndHelpers diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs index 36eb4c5c..a57c6f54 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs @@ -1,12 +1,12 @@ using System.Collections.Immutable; using System.Text; +using EventSourcing.Backbone.SrcGen.Generators.Entities; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using EventSourcing.Backbone.SrcGen.Generators.Entities; - using static EventSourcing.Backbone.Helper; namespace EventSourcing.Backbone From cfe32cc3fdef92a075d9501b89c8f57ff195fcdb Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 21 May 2023 10:20:19 +0000 Subject: [PATCH 009/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 96d7f484..cb21c8ab 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.20 + 1.2.21 From 3de2544d10e8dc5eac412d7f8de8211185adf957 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 21 May 2023 10:20:34 +0000 Subject: [PATCH 010/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cb21c8ab..a018d57d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.21 + 1.2.22 @@ -44,7 +44,7 @@ - + From f7ec41fd229a6e5975611fe586fdcd290e4c0bca Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 21 May 2023 17:13:43 +0300 Subject: [PATCH 011/178] rfc: api --- ...bone.Channels.RedisConsumerProvider.csproj | 2 +- .../RedisConsumerBuilder.cs | 212 +++++++++++++++++ .../RedisConsumerChannel.cs | 57 ++++- .../RedisConsumerProviderExtensions.cs | 115 --------- ...bone.Channels.RedisProducerProvider.csproj | 2 +- .../RedisProducerBuilder.cs | 172 ++++++++++++++ .../RedisProviderExtensions.cs | 100 -------- .../EventSourceRedisConnectionFacroty.cs | 62 ++--- .../IEventSourceRedisConnectionFacroty.cs | 14 +- .../Factory/IRedisConnectionFacrotyBase.cs | 19 -- .../Factory/RedisClientFactory.cs | 222 ++++++++++++------ .../Factory/RedisConnectionFacrotyBase.cs | 80 ++++--- .../Factory/RedisCredentialsKeys.cs | 18 +- .../DeleteKeysTests.cs | 4 +- .../EndToEndExplicitTests.cs | 6 +- .../EndToEndStressTests.cs | 24 +- .../EndToEndTests.cs | 23 +- .../HelloWorld/HelloWorldTests.cs | 92 ++++++++ .../HelloWorld/IHello.cs | 13 + .../InheritanceTests.cs | 23 +- .../MigrationReceiverTest.cs | 5 +- .../MigrationTest.cs | 23 +- .../TestsBase.cs | 126 ++++++++++ .../AspCoreExtensions.cs | 2 +- .../Controllers/TestController.cs | 2 +- .../RegisterEventSourceExtensions.cs | 8 +- 26 files changed, 985 insertions(+), 441 deletions(-) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj index f80699c6..3cfb7bf0 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj @@ -3,8 +3,8 @@ + - diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs new file mode 100644 index 00000000..cdbf3a54 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs @@ -0,0 +1,212 @@ +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using StackExchange.Redis; + +namespace EventSourcing.Backbone +{ + public static class RedisConsumerBuilder + { + /// + /// Create REDIS consumer builder. + /// + /// The redis configuration. + /// The setting. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder ( + this ConfigurationOptions options, + RedisConsumerChannelSetting? setting = null) + { + var stg = setting ?? RedisConsumerChannelSetting.Default; + var builder = ConsumerBuilder.Empty; + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) + { + var channel = new RedisConsumerChannel( + logger, + options, + stg); + return channel; + } + } + + /// + /// Create REDIS consumer builder. + /// + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The setting. + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder Create( + string? endpoint = null, + string? password = null, + RedisConsumerChannelSetting? setting = null, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + return configuration.CreateRedisConsumerBuilder(setting); + } + + /// + /// Create REDIS consumer builder. + /// + /// The setting. + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this RedisConsumerChannelSetting setting, + string? endpoint = null, + string? password = null, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + return configuration.CreateRedisConsumerBuilder(setting); + } + + + /// + /// Create REDIS consumer builder. + /// + /// The credentials keys. + /// The setting. + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this RedisCredentialsKeys credentialsKeys, + RedisConsumerChannelSetting? setting = null, + Action? configurationHook = null) + { + var configuration = credentialsKeys.CreateConfigurationOptions(configurationHook); + return configuration.CreateRedisConsumerBuilder(setting); + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The setting. + /// The redis configuration. + /// + public static IConsumerStoreStrategyBuilder UseRedisChannel( + this IConsumerBuilder builder, + RedisConsumerChannelSetting? setting = null, + ConfigurationOptions? redisConfiguration = null) + { + var stg = setting ?? RedisConsumerChannelSetting.Default; + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) + { + var channel = new RedisConsumerChannel( + logger, + redisConfiguration, + stg); + return channel; + } + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// Environment keys of the credentials + /// The setting. + /// + public static IConsumerStoreStrategyBuilder UseRedisChannel( + this IConsumerBuilder builder, + RedisCredentialsKeys credentialsKeys, + RedisConsumerChannelSetting? setting = null) + { + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) + { + var channel = new RedisConsumerChannel( + logger, + credentialsKeys, + setting); + return channel; + } + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The redis client factory. + /// The setting. + /// + internal static IConsumerStoreStrategyBuilder UseRedisChannel( + this IConsumerBuilder builder, + IEventSourceRedisConnectionFacroty redisClientFactory, + RedisConsumerChannelSetting? setting = null) + { + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) + { + var channel = new RedisConsumerChannel( + redisClientFactory, + logger, + setting); + return channel; + } + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The service provider. + /// The setting. + /// + /// redisClient + public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( + this IConsumerBuilder builder, + IServiceProvider serviceProvider, + RedisConsumerChannelSetting? setting = null) + { + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + return builder.UseRedisChannel(connFactory, setting); + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The service provider. + /// The setting. + /// + /// redisClient + public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( + this IServiceProvider serviceProvider, + RedisConsumerChannelSetting? setting = null) + { + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + + var builder = ConsumerBuilder.Empty; + return builder.UseRedisChannel(connFactory, setting); + } + + } +} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 63c4b720..91e43a21 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -25,10 +25,6 @@ internal class RedisConsumerChannel : IConsumerChannelProvider { private const string BEGIN_OF_STREAM = "0000000000000"; /// - /// Max delay - /// - private const int MAX_DELAY = 5000; - /// /// The read by identifier chunk size. /// REDIS don't have option to read direct position (it read from a position, not includes the position itself), /// therefore read should start before the actual position. @@ -76,13 +72,58 @@ public RedisConsumerChannel( /// Environment keys of the credentials public RedisConsumerChannel( ILogger logger, - Action? configuration = null, + ConfigurationOptions? configuration = null, + RedisConsumerChannelSetting? setting = null) : this( + new EventSourceRedisConnectionFacroty( + logger, + configuration), + logger, + setting) + { + } + + /// + /// Initializes a new instance. + /// + /// The logger. + /// Environment keys of the credentials + /// The setting. + /// The configuration hook. + public RedisConsumerChannel( + ILogger logger, + RedisCredentialsKeys credentialsKeys, + RedisConsumerChannelSetting? setting = null, + Action? configurationHook = null) : this( + new EventSourceRedisConnectionFacroty( + credentialsKeys, + logger, + configurationHook), + logger, + setting) + { + } + + /// + /// Initializes a new instance. + /// + /// The logger. + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The setting. + /// The configuration hook. + public RedisConsumerChannel( + ILogger logger, + string? endpoint = null, + string? password = null, RedisConsumerChannelSetting? setting = null, - RedisCredentialsKeys credentialsKeys = default) : this( + Action? configurationHook = null) : this( new EventSourceRedisConnectionFacroty( logger, - configuration, - credentialsKeys), + endpoint, + password, + configurationHook), logger, setting) { diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs deleted file mode 100644 index cc46c490..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerProviderExtensions.cs +++ /dev/null @@ -1,115 +0,0 @@ -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Channels.RedisProvider; - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -using StackExchange.Redis; - -namespace EventSourcing.Backbone -{ - public static class RedisConsumerProviderExtensions - { - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The setting. - /// The redis configuration. - /// Environment keys of the credentials - /// - public static IConsumerStoreStrategyBuilder UseRedisChannel( - this IConsumerBuilder builder, - Func setting, - Action? redisConfiguration = null, - RedisCredentialsKeys credentialsKeys = default) - { - var stg = setting?.Invoke(RedisConsumerChannelSetting.Default); - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - logger, - redisConfiguration, - stg, - credentialsKeys); - return channel; - } - } - - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The setting. - /// Environment keys of the credentials - /// - public static IConsumerStoreStrategyBuilder UseRedisChannel( - this IConsumerBuilder builder, - RedisConsumerChannelSetting? setting = null, - RedisCredentialsKeys credentialsKeys = default) - { - var cfg = setting ?? RedisConsumerChannelSetting.Default; - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - logger, - setting: cfg, - credentialsKeys: credentialsKeys); - return channel; - } - } - - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The redis client factory. - /// The setting. - /// - public static IConsumerStoreStrategyBuilder UseRedisChannel( - this IConsumerBuilder builder, - IEventSourceRedisConnectionFacroty redisClientFactory, - RedisConsumerChannelSetting? setting = null) - { - var cfg = setting ?? RedisConsumerChannelSetting.Default; - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - redisClientFactory, - logger, - setting); - return channel; - } - } - - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The service provider. - /// The setting. - /// - /// redisClient - public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( - this IConsumerBuilder builder, - IServiceProvider serviceProvider, - RedisConsumerChannelSetting? setting = null) - { - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - return builder.UseRedisChannel(connFactory, setting); - } - - } -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj index 06906d64..1f75e7cd 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj @@ -7,8 +7,8 @@ + - diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs new file mode 100644 index 00000000..86518d2b --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs @@ -0,0 +1,172 @@ +using EventSourcing.Backbone.Channels.RedisProvider; +using EventSourcing.Backbone.Private; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using OpenTelemetry.Trace; + +using Polly; + +using StackExchange.Redis; + +namespace EventSourcing.Backbone +{ + public static class RedisProducerBuilder + { + /// + /// Adds the event producer telemetry source (will result in tracing the producer). + /// + /// The builder. + /// + public static TracerProviderBuilder AddEventProducerTelemetry(this TracerProviderBuilder builder) => builder.AddSource(nameof(RedisProducerChannel)); + + /// + /// Uses REDIS producer channel. + /// + /// + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// + /// + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// + /// The resilience policy. + /// The configuration hook. + /// + public static IProducerStoreStrategyBuilder Create( + string? endpoint = null, + string? password = null, + AsyncPolicy? resiliencePolicy = null, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + return CreateRedisProducerBuilder(configuration, resiliencePolicy); + } + + /// + /// Uses REDIS producer channel. + /// + /// The credential. + /// The resilience policy. + /// The configuration hook. + /// + public static IProducerStoreStrategyBuilder CreateRedisProducerBuilder( + this RedisCredentialsKeys credential, + AsyncPolicy? resiliencePolicy = null, + Action? configurationHook = null) + { + var configuration = credential.CreateConfigurationOptions(configurationHook); + return CreateRedisProducerBuilder(configuration, resiliencePolicy); + } + + /// + /// Uses REDIS producer channel. + /// + /// The configuration. + /// The resilience policy. + /// + /// + public static IProducerStoreStrategyBuilder CreateRedisProducerBuilder( + this ConfigurationOptions configuration, + AsyncPolicy? resiliencePolicy = null) + { + var builder = ProducerBuilder.Empty; + return UseRedisChannel(builder, configuration, resiliencePolicy); + } + + /// + /// Uses REDIS producer channel. + /// + /// The builder. + /// The configuration. + /// The resilience policy. + /// + public static IProducerStoreStrategyBuilder UseRedisChannel( + this IProducerBuilder builder, + ConfigurationOptions? configuration = null, + AsyncPolicy? resiliencePolicy = null) + { + var result = builder.UseChannel(LocalCreate); + return result; + + IProducerChannelProvider LocalCreate(ILogger logger) + { + var connFactory = new EventSourceRedisConnectionFacroty(logger, configuration); + var channel = new RedisProducerChannel( + connFactory, + logger ?? EventSourceFallbakLogger.Default, + resiliencePolicy); + return channel; + } + } + + /// + /// Uses REDIS producer channel. + /// This overload is used by the DI + /// + /// The builder. + /// The redis database. + /// The resilience policy. + /// + /// + internal static IProducerStoreStrategyBuilder UseRedisChannel( + this IProducerBuilder builder, + IEventSourceRedisConnectionFacroty redisConnectionFactory, + AsyncPolicy? resiliencePolicy = null) + { + var result = builder.UseChannel(LocalCreate); + return result; + + IProducerChannelProvider LocalCreate(ILogger logger) + { + var channel = new RedisProducerChannel( + redisConnectionFactory, + logger ?? EventSourceFallbakLogger.Default, + resiliencePolicy); + return channel; + } + } + + /// + /// Uses REDIS producer channel by resolving it as a dependency injection from the service-provider. + /// + /// The builder. + /// The service provider. + /// The resilience policy. + /// + public static IProducerStoreStrategyBuilder GetRedisChannelService( + this IProducerBuilder builder, + IServiceProvider serviceProvider, + AsyncPolicy? resiliencePolicy = null) + { + ILogger logger = serviceProvider.GetService>() ?? throw new Exception("Cannot resolve a logger"); + + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + return builder.UseRedisChannel(connFactory, resiliencePolicy); + } + + /// + /// Uses REDIS producer channel by resolving it as a dependency injection from the service-provider. + /// + /// The service provider. + /// The resilience policy. + /// + public static IProducerStoreStrategyBuilder GetRedisChannelService( + this IServiceProvider serviceProvider, + AsyncPolicy? resiliencePolicy = null) + { + ILogger logger = serviceProvider.GetService>() ?? throw new Exception("Cannot resolve a logger"); + + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + var builder = ProducerBuilder.Empty; + return builder.UseRedisChannel(connFactory, resiliencePolicy); + } + } +} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs deleted file mode 100644 index a71fab88..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProviderExtensions.cs +++ /dev/null @@ -1,100 +0,0 @@ -using EventSourcing.Backbone.Channels.RedisProvider; -using EventSourcing.Backbone.Private; - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -using OpenTelemetry.Trace; - -using Polly; - -using StackExchange.Redis; - -namespace EventSourcing.Backbone -{ - public static class RedisProviderExtensions - { - /// - /// Adds the event producer telemetry source (will result in tracing the producer). - /// - /// The builder. - /// - public static TracerProviderBuilder AddEventProducerTelemetry(this TracerProviderBuilder builder) => builder.AddSource(nameof(RedisProducerChannel)); - - /// - /// Uses REDIS producer channel. - /// - /// The builder. - /// The configuration. - /// The resilience policy. - /// Environment keys of the credentials - /// - /// - public static IProducerStoreStrategyBuilder UseRedisChannel( - this IProducerBuilder builder, - Action? configuration = null, - AsyncPolicy? resiliencePolicy = null, - RedisCredentialsKeys credentialsKeys = default) - { - var result = builder.UseChannel(LocalCreate); - return result; - - IProducerChannelProvider LocalCreate(ILogger logger) - { - var connFactory = new EventSourceRedisConnectionFacroty(logger, configuration, credentialsKeys); - var channel = new RedisProducerChannel( - connFactory, - logger ?? EventSourceFallbakLogger.Default, - resiliencePolicy); - return channel; - } - } - - /// - /// Uses REDIS producer channel. - /// - /// The builder. - /// The redis database. - /// The resilience policy. - /// - /// - public static IProducerStoreStrategyBuilder UseRedisChannel( - this IProducerBuilder builder, - IEventSourceRedisConnectionFacroty redisConnectionFactory, - AsyncPolicy? resiliencePolicy = null) - { - var result = builder.UseChannel(LocalCreate); - return result; - - IProducerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisProducerChannel( - redisConnectionFactory, - logger ?? EventSourceFallbakLogger.Default, - resiliencePolicy); - return channel; - } - } - - /// - /// Uses REDIS producer channel. - /// - /// The builder. - /// The service provider. - /// The resilience policy. - /// - public static IProducerStoreStrategyBuilder UseRedisChannelInjection( - this IProducerBuilder builder, - IServiceProvider serviceProvider, - AsyncPolicy? resiliencePolicy = null) - { - ILogger logger = serviceProvider.GetService>() ?? throw new ArgumentNullException(); - - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - return builder.UseRedisChannel(connFactory, resiliencePolicy); - } - } -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs index 8e994f7c..e115e590 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs @@ -12,42 +12,53 @@ namespace EventSourcing.Backbone /// This factory is also responsible of the connection health. /// It will return same connection as long as it healthy. /// - public sealed class EventSourceRedisConnectionFacroty : RedisConnectionFacrotyBase, IEventSourceRedisConnectionFacroty + public sealed class EventSourceRedisConnectionFacroty : RedisConnectionFacrotyBase { #region Ctor - #region Overloads - /// - /// Constructor + /// Initializes a new instance of the class. /// - /// - /// - /// Environment keys of the credentials + /// The logger. + /// The configuration. public EventSourceRedisConnectionFacroty( - ILogger logger, - Action? configuration = null, - RedisCredentialsKeys credentialsKeys = default - ) : this((ILogger)logger, configuration, credentialsKeys) + ILogger logger, + ConfigurationOptions? configuration) : + base(logger, configuration) { } - #endregion // Overloads - /// - /// Constructor + /// Initializes a new instance of the class. /// - /// - /// - /// Environment keys of the credentials + /// The credential. + /// The logger. + /// The configuration hook. public EventSourceRedisConnectionFacroty( + RedisCredentialsKeys credential, ILogger logger, - Action? configuration = null, - RedisCredentialsKeys credentialsKeys = default) : base(logger, configuration, credentialsKeys) + Action? configurationHook = null) : + base(credential, logger, configurationHook) { - //CredentialsKeys = credentialsKeys; } + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The configuration hook. + public EventSourceRedisConnectionFacroty( + ILogger logger, + string? endpoint = null, + string? password = null, + Action? configurationHook = null) : + base(logger, endpoint, password, configurationHook) + { + } #endregion // Ctor @@ -56,17 +67,8 @@ public EventSourceRedisConnectionFacroty( /// /// Gets the kind. /// - protected override string Kind => "Event-Sourcing"; + protected override string Kind { get; } = "Event-Sourcing"; #endregion // Kind - - //#region CredentialsKeys - - ///// - ///// Gets the credentials keys. - ///// - //protected override RedisCredentialsKeys CredentialsKeys { get; } - - //#endregion // CredentialsKeys } } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs index f66e0ae5..490d0ec3 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs @@ -1,9 +1,19 @@ -namespace EventSourcing.Backbone +using StackExchange.Redis; + +namespace EventSourcing.Backbone { /// /// Connection factory /// - public interface IEventSourceRedisConnectionFacroty : IRedisConnectionFacrotyBase + public interface IEventSourceRedisConnectionFacroty { + /// + /// Get a valid connection + /// + Task GetAsync(); + /// + /// Get database + /// + Task GetDatabaseAsync(); } } \ No newline at end of file diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs deleted file mode 100644 index f0c6f104..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisConnectionFacrotyBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using StackExchange.Redis; - -namespace EventSourcing.Backbone -{ - /// - /// Connection factory - /// - public interface IRedisConnectionFacrotyBase - { - /// - /// Get a valid connection - /// - Task GetAsync(); - /// - /// Get database - /// - Task GetDatabaseAsync(); - } -} \ No newline at end of file diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs index 6525868f..e96e0c1a 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs @@ -19,121 +19,207 @@ public static class RedisClientFactory private static readonly string? ASSEMBLY_NAME = Assembly.GetEntryAssembly()?.GetName()?.Name?.ToDash(); private static readonly Version? ASSEMBLY_VERSION = Assembly.GetEntryAssembly()?.GetName()?.Version; + #region CreateConfigurationOptions + /// - /// Blocking Create REDIS client. - /// Exist only for code which don't support async (like ASP.NET setup (AddSingleton)) + /// Create REDIS configuration options. /// - /// The configuration. - /// The credential. + /// + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// + /// + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// + /// A configuration hook. /// - /// Fail to establish REDIS connection - /// Fail to establish REDIS connection - public static IConnectionMultiplexer CreateProviderBlocking( - Action? configuration = null, - RedisCredentialsKeys credential = default) + public static ConfigurationOptions CreateConfigurationOptions( + string? endpoint = null, + string? password = null, + Action? configurationHook = null) { - var task = CreateProviderAsync(null, configuration, credential); - return task.Result; + RedisCredentialsKeys credential = new RedisCredentialsKeys { Endpoint = endpoint }; + if (!string.IsNullOrEmpty(password)) + credential = credential with { Password = password }; + var redis = credential.CreateConfigurationOptions(configurationHook); + return redis; } /// - /// Blocking Create REDIS client. - /// Exist only for code which don't support async (like ASP.NET setup (AddSingleton)) + /// Create REDIS configuration options. /// - /// The logger. - /// The configuration. - /// The credential. + /// The credential's environment keys. + /// A configuration hook. /// - /// Fail to establish REDIS connection - /// Fail to establish REDIS connection - public static IConnectionMultiplexer CreateProviderBlocking( - ILogger logger, - Action? configuration = null, - RedisCredentialsKeys credential = default) + public static ConfigurationOptions CreateConfigurationOptions( + this RedisCredentialsKeys credential, + Action? configurationHook = null) { - var task = CreateProviderAsync(logger, configuration, credential); - return task.Result; + string endpointKey = credential.Endpoint; + string passwordKey = credential.Password; + string endpoint = Environment.GetEnvironmentVariable(endpointKey) ?? endpointKey; + string? password = Environment.GetEnvironmentVariable(passwordKey) ?? passwordKey; + + var sb = new StringBuilder(); + var writer = new StringWriter(sb); + + // https://stackexchange.github.io/StackExchange.Redis/Configuration.html + var configuration = ConfigurationOptions.Parse(endpoint); + configuration.Password = password; + if (configurationHook != null) + configuration.Apply(configurationHook); + return configuration; } + #endregion // CreateConfigurationOptions + + #region CreateProviderAsync + /// /// Create REDIS client. /// - /// The configuration. - /// The credential. + /// The endpoint. + /// The password. + /// The logger. + /// A configuration hook. /// - /// Fail to establish REDIS connection - /// Fail to establish REDIS connection - public static Task CreateProviderAsync( - Action? configuration = null, - RedisCredentialsKeys credential = default) + public static async Task CreateProviderAsync( + string? endpoint = null, + string? password = null, + ILogger? logger = null, + Action? configurationHook = null) { - return CreateProviderAsync(null, configuration, credential); + RedisCredentialsKeys credential = new RedisCredentialsKeys { Endpoint = endpoint }; + if (!string.IsNullOrEmpty(password)) + credential = credential with { Password = password }; + var redis = await credential.CreateProviderAsync(logger, configurationHook); + return redis; } - /// /// Create REDIS client. /// - /// The logger. - /// The configuration. /// The credential's environment keys. + /// The logger. + /// A configuration hook. /// - /// - /// - /// Fail to establish REDIS connection - /// Fail to establish REDIS connection public static async Task CreateProviderAsync( - ILogger? logger, - Action? configuration = null, - RedisCredentialsKeys credential = default) + this RedisCredentialsKeys credential, + ILogger? logger = null, + Action? configurationHook = null) { - string endpointKey = credential.EndpointKey ?? END_POINT_KEY; - string passwordKey = credential.PasswordKey ?? PASSWORD_KEY; - string? endpoint = Environment.GetEnvironmentVariable(endpointKey); + string endpointKey = credential.Endpoint; + string passwordKey = credential.Password; + string endpoint = Environment.GetEnvironmentVariable(endpointKey) ?? endpointKey; try { - if (endpoint == null) + string? password = Environment.GetEnvironmentVariable(passwordKey) ?? passwordKey; + + var sb = new StringBuilder(); + var writer = new StringWriter(sb); + + // https://stackexchange.github.io/StackExchange.Redis/Configuration.html + var configuration = ConfigurationOptions.Parse(endpoint); + configuration.Password = password; + if (configurationHook != null) + configuration.Apply(configurationHook); + var redis = await configuration.CreateProviderAsync(logger); + return redis; + } + catch (Exception ex) + { + if (logger != null) + logger.LogError(ex.FormatLazy(), "REDIS CONNECTION Setting ERROR: {endpoint}", endpoint); + else + Console.WriteLine($"REDIS CONNECTION Setting ERROR: {ex.FormatLazy()}"); + throw; + } + #region Event Handlers + + void OnInternalConnError(object? sender, InternalErrorEventArgs e) + { + if (logger != null) + { + logger.LogError(e.Exception, "REDIS Connection internal failure: Failure type = {typeOfConnection}, Origin = {typeOfFailure}", + e.ConnectionType, e.Origin); + } + else + Console.WriteLine($"REDIS Connection internal failure: Failure type = {e.ConnectionType}, Origin = {e.Origin}"); + } + + void OnConnErrorMessage(object? sender, RedisErrorEventArgs e) + { + if (logger != null) { - #region Throw + Log + logger.LogWarning("REDIS Connection error: {message}", + e.Message); + } + else + Console.WriteLine($"REDIS Connection error: {e.Message}"); + } - if (logger != null) - logger.LogError("REDIS CONNECTION: ENDPOINT [ENV variable: {endpointKey}] is missing", endpointKey); - else - Console.WriteLine($"REDIS CONNECTION: ENDPOINT [ENV variable: {endpointKey}] is missing"); - throw new KeyNotFoundException($"REDIS KEY [ENV variable: {endpointKey}] is missing"); - #endregion // Throw + Log + void OnConnectionFailed(object? sender, ConnectionFailedEventArgs e) + { + if (logger != null) + { + logger.LogError(e.Exception, "REDIS Connection failure: Failure type = {typeOfConnection}, Failure type = {typeOfFailure}", e.ConnectionType, e.FailureType); } + else + Console.WriteLine($"REDIS Connection failure: Failure type = {e.ConnectionType}, Failure type = {e.FailureType}"); + } - string? password = Environment.GetEnvironmentVariable(passwordKey); + #endregion // Event Handlers + } + /// + /// Create REDIS client. + /// + /// The logger. + /// The configuration. + /// The credential's environment keys. + /// + /// + /// + /// Fail to establish REDIS connection + /// Fail to establish REDIS connection + public static async Task CreateProviderAsync( + this ConfigurationOptions configuration, + ILogger? logger = null) + { + try + { var sb = new StringBuilder(); var writer = new StringWriter(sb); // https://stackexchange.github.io/StackExchange.Redis/Configuration.html - var redisConfiguration = ConfigurationOptions.Parse(endpoint); - redisConfiguration.ClientName = string.Format( + configuration.ClientName = string.Format( CONNECTION_NAME_PATTERN, ASSEMBLY_NAME, ASSEMBLY_VERSION, Interlocked.Increment(ref _index)); - configuration?.Invoke(redisConfiguration); - redisConfiguration.Password = password; - // keep retry to get connection on failure - redisConfiguration.AbortOnConnectFail = false; - //redisConfiguration.ConnectTimeout = 15; - //redisConfiguration.SyncTimeout = 10; - //redisConfiguration.AsyncTimeout = 10; - //redisConfiguration.DefaultDatabase = Debugger.IsAttached ? 1 : null; + configuration = configuration.Apply(cfg => + { + // keep retry to get connection on failure + cfg.AbortOnConnectFail = false; + //cfg.ConnectTimeout = 15; + //cfg.SyncTimeout = 10; + //cfg.AsyncTimeout = 10; + //cfg.DefaultDatabase = Debugger.IsAttached ? 1 : null; + }); - IConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync(redisConfiguration, writer); + IConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync(configuration, writer); + string endpoints = string.Join(";", configuration.EndPoints); if (logger != null) - logger.LogInformation("REDIS Connection [{envKey}]: {info} succeed", endpointKey, sb); + logger.LogInformation("REDIS Connection [{envKey}]: {info} succeed", + endpoints, + sb); else - Console.WriteLine($"REDIS Connection [{endpointKey}] succeed: {sb}"); + Console.WriteLine($"REDIS Connection [{endpoints}] succeed: {sb}"); redis.ConnectionFailed += OnConnectionFailed; redis.ErrorMessage += OnConnErrorMessage; redis.InternalError += OnInternalConnError; @@ -187,5 +273,7 @@ void OnConnectionFailed(object? sender, ConnectionFailedEventArgs e) #endregion // Event Handlers } + + #endregion // CreateProviderAsync } } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs index 3d07d689..652b71b7 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Logging; +using System.Net; + +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -12,13 +14,12 @@ namespace EventSourcing.Backbone /// This factory is also responsible of the connection health. /// It will return same connection as long as it healthy. /// - public abstract class RedisConnectionFacrotyBase : IRedisConnectionFacrotyBase, IDisposable, IAsyncDisposable + public abstract class RedisConnectionFacrotyBase : IEventSourceRedisConnectionFacroty, IDisposable, IAsyncDisposable { private const int CLOSE_DELEY_MILLISECONDS = 5000; private Task _redisTask; private readonly ILogger _logger; - private readonly Action? _configuration; - private readonly RedisCredentialsKeys _credentialsKeys; + private readonly ConfigurationOptions _configuration; private readonly AsyncLock _lock = new AsyncLock(TimeSpan.FromSeconds(CLOSE_DELEY_MILLISECONDS)); private DateTime _lastResetConnection = DateTime.Now; private int _reconnectTry = 0; @@ -31,16 +32,47 @@ public abstract class RedisConnectionFacrotyBase : IRedisConnectionFacrotyBase, /// Constructor /// /// The logger. - /// The configuration. - /// The credentials keys. - public RedisConnectionFacrotyBase( - ILogger logger, - Action? configuration = null, - RedisCredentialsKeys credentialsKeys = default - ) : this((ILogger)logger, configuration, credentialsKeys) + /// + /// Create REDIS configuration options. + /// + /// + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. + /// + /// + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// + /// The configuration hook. + protected RedisConnectionFacrotyBase( + ILogger logger, + string? endpoint = null, + string? password = null, + Action? configurationHook = null) + { + _logger = logger; + _configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); + } + + + /// + /// Constructor + /// + /// The credential. + /// The logger. + /// The configuration hook. + protected RedisConnectionFacrotyBase( + RedisCredentialsKeys credential, + ILogger logger, + Action? configurationHook = null) { + _logger = logger; + _configuration = credential.CreateConfigurationOptions(configurationHook); + _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); } + #endregion // Overloads /// @@ -48,30 +80,18 @@ public RedisConnectionFacrotyBase( /// /// The logger. /// The configuration. - /// The credentials keys. - public RedisConnectionFacrotyBase( + protected RedisConnectionFacrotyBase( ILogger logger, - Action? configuration = null, - RedisCredentialsKeys credentialsKeys = default) + ConfigurationOptions? configuration) { _logger = logger; _configuration = configuration; - _credentialsKeys = credentialsKeys; - _redisTask = RedisClientFactory.CreateProviderAsync(logger, configuration, credentialsKeys); + _redisTask = RedisClientFactory.CreateProviderAsync(configuration, logger); } #endregion // Ctor - //#region CredentialsKeys - - ///// - ///// Gets the credentials keys. - ///// - //protected abstract RedisCredentialsKeys CredentialsKeys { get; } - - //#endregion // CredentialsKeys - #region Kind /// @@ -86,7 +106,7 @@ public RedisConnectionFacrotyBase( /// /// Get a valid connection /// - async Task IRedisConnectionFacrotyBase.GetAsync() + async Task IEventSourceRedisConnectionFacroty.GetAsync() { var conn = await _redisTask; if (conn.IsConnected) @@ -110,7 +130,7 @@ async Task IRedisConnectionFacrotyBase.GetAsync() _lastResetConnection = DateTime.Now; var cn = conn; Task _ = Task.Delay(CLOSE_DELEY_MILLISECONDS).ContinueWith(_ => cn.CloseAsync()); - _redisTask = RedisClientFactory.CreateProviderAsync(_logger, _configuration, _credentialsKeys); + _redisTask = _configuration.CreateProviderAsync(_logger); var newConn = await _redisTask; return newConn; } @@ -125,9 +145,9 @@ async Task IRedisConnectionFacrotyBase.GetAsync() /// /// Get database /// - async Task IRedisConnectionFacrotyBase.GetDatabaseAsync() + async Task IEventSourceRedisConnectionFacroty.GetDatabaseAsync() { - IRedisConnectionFacrotyBase self = this; + IEventSourceRedisConnectionFacroty self = this; IConnectionMultiplexer conn = await self.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); return db; diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs index 4812f0b6..dd0736b6 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs @@ -6,21 +6,17 @@ namespace EventSourcing.Backbone /// /// Environment keys for REDIS's credentials /// - public readonly record struct RedisCredentialsKeys + public record RedisCredentialsKeys { - public RedisCredentialsKeys() - { - EndpointKey = END_POINT_KEY; - PasswordKey = PASSWORD_KEY; - } - /// - /// Endpoint Key + /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. /// - public string EndpointKey { get; init; } + public string Endpoint { get; init; } = END_POINT_KEY; /// - /// Password Key + /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). + /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. /// - public string PasswordKey { get; init; } + public string Password { get; init; } = PASSWORD_KEY; } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs index 1cac0fad..51f1dd0e 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs @@ -29,8 +29,8 @@ public DeleteKeysTests( [Trait("type", "delete-keys")] public async Task DELETE_KEYS_TEST(string pattern) { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - cfg => cfg.AllowAdmin = true); + IConnectionMultiplexer conn = await RedisClientFactory.CreateProviderAsync( + configurationHook: cfg => cfg.AllowAdmin = true); string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); IEnumerable keys = server.Keys(pattern: pattern).ToArray(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 5faffe7e..2c4c64d1 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -183,9 +183,9 @@ public void Dispose() { GC.SuppressFinalize(this); string key = URI; - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - _fakeLogger, - cfg => cfg.AllowAdmin = true); + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; IDatabaseAsync db = conn.GetDatabase(); db.KeyDeleteAsync(key, CommandFlags.DemandMaster).Wait(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index cf6e6934..f4497daf 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; using EventSourcing.Backbone.UnitTests.Entities; using FakeItEasy; @@ -45,15 +46,14 @@ public EndToEndStressTests( configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; - var consumerBuilder = ConsumerBuilder.Empty.UseRedisChannel( - stg => stg with - { - DelayWhenEmptyBehavior = - stg.DelayWhenEmptyBehavior with - { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) - } - }); + var stg = new RedisConsumerChannelSetting + { + DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior + { + CalcNextDelay =(d => TimeSpan.FromMilliseconds(2)) + } + }; + var consumerBuilder = stg.CreateRedisConsumerBuilder(); consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; var claimTrigger = new ClaimingTrigger { EmptyBatchCount = 5, MinIdleTime = TimeSpan.FromSeconds(3) }; _consumerBuilder = consumerBuilder.WithOptions(o => o with { ClaimingTrigger = claimTrigger }); @@ -244,8 +244,10 @@ public void Dispose() GC.SuppressFinalize(this); try { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - cfg => cfg.AllowAdmin = true); + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true) + .Result; string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); IEnumerable keys = server.Keys(pattern: $"*{URI}*"); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 82ede346..7e9ec228 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; using EventSourcing.Backbone.Enums; using EventSourcing.Backbone.UnitTests.Entities; @@ -67,15 +68,14 @@ public EndToEndTests( _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; - var consumerBuilder = ConsumerBuilder.Empty.UseRedisChannel( - stg => stg with - { - DelayWhenEmptyBehavior = - stg.DelayWhenEmptyBehavior with - { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) - } - }); + var stg = new RedisConsumerChannelSetting + { + DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior + { + CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + } + }; + var consumerBuilder = stg.CreateRedisConsumerBuilder(); _consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) @@ -1756,8 +1756,9 @@ public void Dispose() GC.SuppressFinalize(this); try { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - cfg => cfg.AllowAdmin = true); + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); IEnumerable keys = server.Keys(pattern: $"*{URI}*"); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs new file mode 100644 index 00000000..153f5864 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs @@ -0,0 +1,92 @@ +using System.Diagnostics; +using System.Text.Json; +using System.Threading.Tasks.Dataflow; + +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.Tests; +using EventSourcing.Backbone.UnitTests.Entities; + +using FakeItEasy; + +using Microsoft.Extensions.Logging; + +using Polly; + +using StackExchange.Redis; + +using Xunit; +using Xunit.Abstractions; + +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.EventSourceConstants; + +// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest + +namespace EventSourcing.Backbone.IntegrationTests.HelloWorld +{ + /// + /// The end to end tests. + /// + public class HelloWorldTests : TestsBase + { + protected override string URI { get; } = $"demo:{DateTimeOffset.Now.ToUnixTimeMilliseconds}"; + private readonly IHelloConsumer _subscriber = A.Fake(); + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + /// The output helper. + public HelloWorldTests(ITestOutputHelper outputHelper) : base(outputHelper) + { + } + + #endregion // Ctor + + #region HelloWorld_Test + + [Fact(Timeout = TIMEOUT)] + public async Task HelloWorld_Test() + { + IHelloProducer producer = RedisProducerBuilder.Create() + .Environment("testing") + .Uri(URI) + .WithLogger(_fakeLogger) + .BuildHelloProducer(); + + var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + IConsumerLifetime subscription = RedisConsumerBuilder.Create() + .WithOptions(o => new ConsumerOptions + { + MaxMessages = 2, // disconnect after consuming 2 messages + AckBehavior = AckBehavior.OnSucceed + }) + .WithCancellation(cancellation.Token) + .Environment("testing") + .Uri(URI) + .WithLogger(_fakeLogger) + .Group("CONSUMER_GROUP_1") // the consumer group + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") // the name of the specific consumer + .SubscribeHelloConsumer(_subscriber); + + await producer.HelloAsync("Hi"); + await producer.WorldAsync(5); + + + await subscription.Completion; + + #region Validation + + A.CallTo(() => _subscriber.HelloAsync("Hi")) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.WorldAsync(5)) + .MustHaveHappenedOnceExactly(); + + #endregion // Validation + } + + #endregion // HelloWorld_Test + } +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs new file mode 100644 index 00000000..032fe76a --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs @@ -0,0 +1,13 @@ +namespace EventSourcing.Backbone.UnitTests.Entities +{ + /// + /// The sequence operations. + /// + [GenerateEventSource(EventSourceGenType.Producer)] + [GenerateEventSource(EventSourceGenType.Consumer)] + public interface IHello + { + ValueTask HelloAsync(string message); + ValueTask WorldAsync(int value); + } +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index a27bf361..cad2f6ea 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; using EventSourcing.Backbone.Enums; using FakeItEasy; @@ -58,15 +59,14 @@ public InheritanceTests( _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; - var consumerBuilder = ConsumerBuilder.Empty.UseRedisChannel( - stg => stg with - { - DelayWhenEmptyBehavior = - stg.DelayWhenEmptyBehavior with - { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) - } - }); + var stg = new RedisConsumerChannelSetting + { + DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior + { + CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + } + }; + var consumerBuilder = stg.CreateRedisConsumerBuilder(); _consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; } @@ -314,8 +314,9 @@ public void Dispose() GC.SuppressFinalize(this); try { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - cfg => cfg.AllowAdmin = true); + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs index 64cbb290..9a2ba4eb 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -38,10 +38,11 @@ public class MigrationReceiverTest // : IDisposable public MigrationReceiverTest(ITestOutputHelper outputHelper) { _outputHelper = outputHelper; - _targetProducerBuilder = ProducerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsKeys { EndpointKey = TARGET_KEY }) + var credentials = new RedisCredentialsKeys { Endpoint = TARGET_KEY }; + _targetProducerBuilder = credentials.CreateRedisProducerBuilder() .AddVoidStrategy(); - _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsKeys { EndpointKey = SOURCE_KEY }) + _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsKeys { Endpoint = SOURCE_KEY }) .AddS3Strategy(new S3Options { Bucket = "event-source-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 88c95741..8c7bb991 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; using EventSourcing.Backbone.Enums; using EventSourcing.Backbone.UnitTests.Entities; @@ -50,15 +51,14 @@ public MigrationTest( _outputHelper = outputHelper; _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); - _consumerBuilder = ConsumerBuilder.Empty.UseRedisChannel( - stg => stg with - { - DelayWhenEmptyBehavior = - stg.DelayWhenEmptyBehavior with - { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) - } - }); + var stg = new RedisConsumerChannelSetting + { + DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior + { + CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + } + }; + _consumerBuilder = stg.CreateRedisConsumerBuilder(); A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) .ReturnsLazily(() => @@ -270,8 +270,9 @@ public void Dispose() GC.SuppressFinalize(this); try { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderBlocking( - cfg => cfg.AllowAdmin = true); + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs new file mode 100644 index 00000000..6c1a6f8f --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs @@ -0,0 +1,126 @@ +using System.Diagnostics; +using System.Text.Json; +using System.Threading.Tasks.Dataflow; + +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.IntegrationTests.HelloWorld; +using EventSourcing.Backbone.UnitTests.Entities; + +using FakeItEasy; + +using Microsoft.Extensions.Logging; + +using Polly; + +using StackExchange.Redis; + +using Xunit; +using Xunit.Abstractions; + +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.EventSourceConstants; + +// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest + +namespace EventSourcing.Backbone.Tests +{ + /// + /// The end to end tests. + /// + public abstract class TestsBase : IDisposable + { + protected readonly ITestOutputHelper _outputHelper; + protected readonly ILogger _fakeLogger = A.Fake(); + protected const int TIMEOUT = 1_000 * 50; + + protected abstract string URI { get; } + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + /// The output helper. + public TestsBase(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + } + + #endregion // Ctor + + #region Dispose pattern + + + ~TestsBase() + { + Dispose(); + } + + public void Dispose() + { + try + { + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; + string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; + var server = conn.GetServer(serverName); + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); + IDatabaseAsync db = conn.GetDatabase(); + + var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8604 // Possible null reference argument. + foreach (string key in keys) + { + ab.Post(key); + } +#pragma warning restore CS8604 // Possible null reference argument. +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. + + ab.Complete(); + ab.Completion.Wait(); + + async Task LocalAsync(string k) + { + try + { + await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); + _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); + } + #region Exception Handling + + catch (RedisTimeoutException ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + catch (Exception ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + + #endregion // Exception Handling + } + } + #region Exception Handling + + catch (RedisTimeoutException ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + catch (Exception ex) + { + _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); + } + + #endregion // Exception Handling + finally + { + GC.SuppressFinalize(this); + } + } + + #endregion // Dispose pattern + } +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index f02b2cab..ae29be7d 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -39,7 +39,7 @@ public static IConnectionMultiplexer AddRedis( IHostEnvironment hostEnv, string shortAppName) { - IConnectionMultiplexer redisConnection = RedisClientFactory.CreateProviderBlocking(); + IConnectionMultiplexer redisConnection = RedisClientFactory.CreateProviderAsync().Result; services.AddSingleton(redisConnection); return redisConnection; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs index 0163acf5..a58bd197 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs @@ -29,7 +29,7 @@ public TestController( [ProducesResponseType(StatusCodes.Status201Created)] public async ValueTask GetAsync() { - var conn = await RedisClientFactory.CreateProviderAsync(_logger); + var conn = await RedisClientFactory.CreateProviderAsync(logger: _logger); var status = conn.GetStatus(); var db = conn.GetDatabase(); var p = await db.PingAsync(); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 256a6e9a..561fef87 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -21,7 +21,7 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { ILogger logger = ioc.GetService>() ?? throw new ArgumentNullException(); - IRawProducer producer = ProducerBuilder.Empty.UseRedisChannelInjection(ioc) + IRawProducer producer = ProducerBuilder.Empty.GetRedisChannelService(ioc) // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .BuildRaw(); return producer; @@ -29,7 +29,7 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { ILogger logger = ioc.GetService>() ?? throw new ArgumentNullException(); - IEventFlowProducer producer = ProducerBuilder.Empty.UseRedisChannelInjection(ioc) + IEventFlowProducer producer = ProducerBuilder.Empty.GetRedisChannelService(ioc) // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .Environment(env) .Uri(PARTITION) @@ -40,7 +40,7 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { IConsumerReadyBuilder consumer = - ConsumerBuilder.Empty.UseRedisChannelInjection(ioc) + ioc.UseRedisChannelInjection() // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { @@ -54,7 +54,7 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { IConsumerHooksBuilder consumer = - ConsumerBuilder.Empty.UseRedisChannelInjection(ioc) + ioc.UseRedisChannelInjection() // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { From 2d98b48fb9e31098bb00287a3d0ed27139955b0f Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 21 May 2023 14:14:10 +0000 Subject: [PATCH 012/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index a018d57d..6bb74366 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.22 + 1.2.23 From 54f3c6fcd7000cda7a6cd26ed9a3d72b4ef658d5 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 21 May 2023 14:14:26 +0000 Subject: [PATCH 013/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 6bb74366..71f9edf6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.23 + 1.2.24 @@ -44,7 +44,7 @@ - + From 16a216e13c9809ffb10a7ca956dc6d12d0bbfa1a Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 07:42:17 +0300 Subject: [PATCH 014/178] rfc: builder init --- .../RedisConsumerBuilder.cs | 52 ++++-- .../RedisConsumerChannel.cs | 112 +++---------- .../RedisProducerBuilder.cs | 25 ++- .../EventSourceRedisConnectionFacroty.cs | 18 +- .../Factory/IRedisCredentials.cs | 11 ++ .../Factory/RedisClientFactory.cs | 151 ++++++++--------- .../Factory/RedisConnectionFacrotyBase.cs | 53 +++--- ...ialsKeys.cs => RedisCredentialsEnvKeys.cs} | 8 +- .../Factory/RedisCredentialsRaw.cs | 36 ++++ .../Building/IConsumerSubscribeBuilder.cs | 2 +- .../DeleteKeysTests.cs | 1 - .../EndToEndExplicitTests.cs | 26 +-- .../EndToEndStressTests.cs | 35 +--- .../EndToEndTests.cs | 155 ++---------------- .../HelloWorld/HelloWorldTests.cs | 80 +++++++++ .../InheritanceTests.cs | 4 +- .../MigrationReceiverTest.cs | 4 +- .../S3StoreStrategyStressTests .cs | 1 - .../TestsBase.cs | 4 +- 19 files changed, 348 insertions(+), 430 deletions(-) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisCredentials.cs rename Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/{RedisCredentialsKeys.cs => RedisCredentialsEnvKeys.cs} (56%) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs index cdbf3a54..e37a10db 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs @@ -11,6 +11,33 @@ namespace EventSourcing.Backbone { public static class RedisConsumerBuilder { + /// + /// Create REDIS consumer builder. + /// + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder Create( + string endpoint, + string? password = null, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + return configuration.CreateRedisConsumerBuilder(); + } + /// + /// Create REDIS consumer builder. + /// + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder Create( + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); + return configuration.CreateRedisConsumerBuilder(); + } + /// /// Create REDIS consumer builder. /// @@ -39,20 +66,14 @@ IConsumerChannelProvider LocalCreate(ILogger logger) /// /// Create REDIS consumer builder. /// - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. /// The setting. /// The configuration hook. /// - public static IConsumerStoreStrategyBuilder Create( - string? endpoint = null, - string? password = null, - RedisConsumerChannelSetting? setting = null, + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this RedisConsumerChannelSetting setting, Action? configurationHook = null) { - var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); return configuration.CreateRedisConsumerBuilder(setting); } @@ -60,15 +81,13 @@ public static IConsumerStoreStrategyBuilder Create( /// Create REDIS consumer builder. /// /// The setting. - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). /// The configuration hook. /// public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( this RedisConsumerChannelSetting setting, - string? endpoint = null, + string endpoint, string? password = null, Action? configurationHook = null) { @@ -85,7 +104,7 @@ public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( /// The configuration hook. /// public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( - this RedisCredentialsKeys credentialsKeys, + this RedisCredentialsEnvKeys credentialsKeys, RedisConsumerChannelSetting? setting = null, Action? configurationHook = null) { @@ -128,7 +147,7 @@ IConsumerChannelProvider LocalCreate(ILogger logger) /// public static IConsumerStoreStrategyBuilder UseRedisChannel( this IConsumerBuilder builder, - RedisCredentialsKeys credentialsKeys, + RedisCredentialsEnvKeys credentialsKeys, RedisConsumerChannelSetting? setting = null) { var channelBuilder = builder.UseChannel(LocalCreate); @@ -191,7 +210,6 @@ public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( /// /// Uses REDIS consumer channel. /// - /// The builder. /// The service provider. /// The setting. /// diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 91e43a21..8dcf9506 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -69,7 +69,6 @@ public RedisConsumerChannel( /// The logger. /// The configuration. /// The setting. - /// Environment keys of the credentials public RedisConsumerChannel( ILogger logger, ConfigurationOptions? configuration = null, @@ -91,7 +90,7 @@ public RedisConsumerChannel( /// The configuration hook. public RedisConsumerChannel( ILogger logger, - RedisCredentialsKeys credentialsKeys, + IRedisCredentials credentialsKeys, RedisConsumerChannelSetting? setting = null, Action? configurationHook = null) : this( new EventSourceRedisConnectionFacroty( @@ -107,15 +106,13 @@ public RedisConsumerChannel( /// Initializes a new instance. /// /// The logger. - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). /// The setting. /// The configuration hook. public RedisConsumerChannel( ILogger logger, - string? endpoint = null, + string endpoint, string? password = null, RedisConsumerChannelSetting? setting = null, Action? configurationHook = null) : this( @@ -159,8 +156,7 @@ public async ValueTask SubsribeAsync( try { await SubsribeToSingleAsync(plan, func, options, joinCancellation); - //else - // await SubsribePartitionAsync(plan, func, options, joinCancellation); + // TODO: [bnaya 2023-05-22] think of the api for multi stream subscription (by pattern) -> var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*").WithCancellation(cancellationToken) if (options.FetchUntilUnixDateOrEmpty != null) break; @@ -192,72 +188,6 @@ public async ValueTask SubsribeAsync( #endregion // SubsribeAsync - #region SubsribePartitionAsync - - ///// - ///// Subscribe to all shards under a partition. - ///// - ///// The consumer plan. - ///// The function. - ///// The options. - ///// The cancellation token. - ///// - ///// When completed - ///// - //private async ValueTask SubsribePartitionAsync( - // IConsumerPlan plan, - // Func> func, - // ConsumerOptions options, - // CancellationToken cancellationToken) - //{ - // var subscriptions = new Queue(); - // int delay = 1; - // string partition = plan.Uri; - // int partitionSplit = partition.Length + 1; - // while (!cancellationToken.IsCancellationRequested) - // { // loop for error cases - // try - // { - // // infinite until cancellation (return unique shareds) - // var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*") - // .WithCancellation(cancellationToken); - - // await foreach (string key in keys) - // { - // string shard = key.Substring(partitionSplit); - // IConsumerPlan p = plan.WithShard(shard); - // // infinite task (until cancellation) - // Task subscription = SubsribeToSingleAsync(plan, func, options, cancellationToken); - // subscriptions.Enqueue(subscription); - // } - - // break; - // } - // catch (Exception ex) - // { - // plan.Logger.LogError(ex, "Partition subscription"); - // await DelayIfRetry(); - // } - // } - - // // run until cancellation or error - // await Task.WhenAll(subscriptions); - - // #region DelayIfRetry - - // async Task DelayIfRetry() - // { - // await Task.Delay(delay, cancellationToken); - // delay *= Max(delay, 2); - // delay = Min(MAX_DELAY, delay); - // } - - // #endregion // DelayIfRetry - - //} - - #endregion // SubsribePartitionAsync - #region SubsribeToSingleAsync /// @@ -276,7 +206,7 @@ private async Task SubsribeToSingleAsync( var claimingTrigger = options.ClaimingTrigger; var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - string key = plan.FullUri(); // $"{plan.Partition}:{plan.Shard}"; + string key = plan.FullUri(); bool isFirstBatchOrFailure = true; CommandFlags flags = CommandFlags.None; @@ -303,8 +233,11 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( TimeSpan delay = TimeSpan.Zero; int emptyBatchCount = 0; - while (!cancellationToken.IsCancellationRequested && await HandleBatchAsync()) + while (!cancellationToken.IsCancellationRequested) { + var proceed = await HandleBatchAsync(); + if (!proceed) + break; } #region HandleBatchAsync @@ -356,7 +289,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) if (string.IsNullOrEmpty(metaJson)) { // backward comparability - string channelType = ((string?)channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]) ?? throw new ArgumentNullException(nameof(MetadataExtensions.Empty.ChannelType)); + string channelType = ((string?)channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]) ?? throw new Exception(nameof(MetadataExtensions.Empty.ChannelType)); if (channelType != CHANNEL_TYPE) { @@ -366,8 +299,8 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) continue; } - string id = ((string?)channelMeta[nameof(MetadataExtensions.Empty.MessageId)]) ?? throw new ArgumentNullException(nameof(MetadataExtensions.Empty.MessageId)); - string operation = ((string?)channelMeta[nameof(MetadataExtensions.Empty.Operation)]) ?? throw new ArgumentNullException(nameof(MetadataExtensions.Empty.Operation)); + string id = ((string?)channelMeta[nameof(MetadataExtensions.Empty.MessageId)]) ?? throw new Exception(nameof(MetadataExtensions.Empty.MessageId)); + string operation = ((string?)channelMeta[nameof(MetadataExtensions.Empty.Operation)]) ?? throw new Exception(nameof(MetadataExtensions.Empty.Operation)); long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); if (fetchUntil != null && string.Compare(fetchUntil, result.Id) < 0) @@ -385,9 +318,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) } else { - //byte[] metabytes = Convert.FromBase64String(meta64); - //string metaJson = Encoding.UTF8.GetString(metabytes); - meta = JsonSerializer.Deserialize(metaJson, EventSourceOptions.FullSerializerOptions) ?? throw new ArgumentNullException(nameof(Metadata)); //, EventSourceJsonContext..Metadata); + meta = JsonSerializer.Deserialize(metaJson, EventSourceOptions.FullSerializerOptions) ?? throw new Exception(nameof(Metadata)); meta = meta with { EventKey = eventKey }; } @@ -703,13 +634,15 @@ async ValueTask AckAsync(RedisValue messageId) IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); // release the event (won't handle again in the future) - long id = await db.StreamAcknowledgeAsync(key, + await db.StreamAcknowledgeAsync(key, plan.ConsumerGroup, messageId, flags: CommandFlags.DemandMaster); } - catch (Exception) - { // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class + catch(Exception ex) + { + // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class + logger.LogWarning(ex.FormatLazy(), $"Fail to acknowledge message [{messageId}]"); throw; } } @@ -793,7 +726,6 @@ async ValueTask IConsumerChannelProvider.GetByIdAsync( { IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); - ILogger logger = plan.Logger; StreamEntry entry = await FindAsync(entryId); #region var announcement = new Announcement(...) @@ -921,11 +853,8 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable ConsumerAsyncEnumerableOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { - string mtdName = $"{nameof(IConsumerChannelProvider)}.{nameof(IConsumerChannelProvider.GetAsyncEnumerable)}"; - IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); - ILogger logger = plan.Logger; var loop = AsyncLoop().WithCancellation(cancellationToken); await foreach (StreamEntry entry in loop) { @@ -936,7 +865,6 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable Dictionary channelMeta = entry.Values.ToDictionary(m => m.Name, m => m.Value); #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning disable CS8601 // Possible null reference assignment. - string channelType = channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]; string id = channelMeta[nameof(MetadataExtensions.Empty.MessageId)]; string operation = channelMeta[nameof(MetadataExtensions.Empty.Operation)]; long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; @@ -969,7 +897,7 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable #endregion // var announcement = new Announcement(...) yield return announcement; - }; + } #region AsyncLoop diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs index 86518d2b..21567a1f 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs @@ -22,6 +22,20 @@ public static class RedisProducerBuilder /// public static TracerProviderBuilder AddEventProducerTelemetry(this TracerProviderBuilder builder) => builder.AddSource(nameof(RedisProducerChannel)); + /// + /// Uses REDIS producer channel. + /// + /// The resilience policy. + /// The configuration hook. + /// + public static IProducerStoreStrategyBuilder Create( + AsyncPolicy? resiliencePolicy = null, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); + return CreateRedisProducerBuilder(configuration, resiliencePolicy); + } + /// /// Uses REDIS producer channel. /// @@ -37,7 +51,7 @@ public static class RedisProducerBuilder /// The configuration hook. /// public static IProducerStoreStrategyBuilder Create( - string? endpoint = null, + string endpoint, string? password = null, AsyncPolicy? resiliencePolicy = null, Action? configurationHook = null) @@ -54,7 +68,7 @@ public static IProducerStoreStrategyBuilder Create( /// The configuration hook. /// public static IProducerStoreStrategyBuilder CreateRedisProducerBuilder( - this RedisCredentialsKeys credential, + this IRedisCredentials credential, AsyncPolicy? resiliencePolicy = null, Action? configurationHook = null) { @@ -74,7 +88,8 @@ public static IProducerStoreStrategyBuilder CreateRedisProducerBuilder( AsyncPolicy? resiliencePolicy = null) { var builder = ProducerBuilder.Empty; - return UseRedisChannel(builder, configuration, resiliencePolicy); + + return builder.UseRedisChannel(configuration, resiliencePolicy); } /// @@ -142,8 +157,6 @@ public static IProducerStoreStrategyBuilder GetRedisChannelService( IServiceProvider serviceProvider, AsyncPolicy? resiliencePolicy = null) { - ILogger logger = serviceProvider.GetService>() ?? throw new Exception("Cannot resolve a logger"); - var connFactory = serviceProvider.GetService(); if (connFactory == null) throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); @@ -160,8 +173,6 @@ public static IProducerStoreStrategyBuilder GetRedisChannelService( this IServiceProvider serviceProvider, AsyncPolicy? resiliencePolicy = null) { - ILogger logger = serviceProvider.GetService>() ?? throw new Exception("Cannot resolve a logger"); - var connFactory = serviceProvider.GetService(); if (connFactory == null) throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs index e115e590..667507c7 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs @@ -17,13 +17,13 @@ public sealed class EventSourceRedisConnectionFacroty : RedisConnectionFacrotyBa #region Ctor /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The logger. /// The configuration. + /// The logger. public EventSourceRedisConnectionFacroty( ILogger logger, - ConfigurationOptions? configuration) : + ConfigurationOptions? configuration = null) : base(logger, configuration) { } @@ -35,7 +35,7 @@ public EventSourceRedisConnectionFacroty( /// The logger. /// The configuration hook. public EventSourceRedisConnectionFacroty( - RedisCredentialsKeys credential, + IRedisCredentials credential, ILogger logger, Action? configurationHook = null) : base(credential, logger, configurationHook) @@ -46,20 +46,20 @@ public EventSourceRedisConnectionFacroty( /// Initializes a new instance of the class. /// /// The logger. - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). /// The configuration hook. public EventSourceRedisConnectionFacroty( ILogger logger, - string? endpoint = null, + string endpoint, string? password = null, Action? configurationHook = null) : base(logger, endpoint, password, configurationHook) { } + + #endregion // Ctor #region Kind diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisCredentials.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisCredentials.cs new file mode 100644 index 00000000..87728c96 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IRedisCredentials.cs @@ -0,0 +1,11 @@ +namespace EventSourcing.Backbone +{ + /// + /// Redis credentials abstraction + /// + public interface IRedisCredentials + { + string? Endpoint { get; } + string? Password { get; } + } +} \ No newline at end of file diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs index e96e0c1a..f74c7fc8 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Diagnostics; +using System.Reflection; using System.Text; using Microsoft.Extensions.Logging; @@ -24,24 +25,29 @@ public static class RedisClientFactory /// /// Create REDIS configuration options. /// - /// - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. - /// - /// - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. - /// /// A configuration hook. /// public static ConfigurationOptions CreateConfigurationOptions( - string? endpoint = null, + Action? configurationHook = null) + { + IRedisCredentials credential = new RedisCredentialsEnvKeys(); + var redis = credential.CreateConfigurationOptions(configurationHook); + return redis; + } + + /// + /// Create REDIS configuration options. + /// + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// A configuration hook. + /// + public static ConfigurationOptions CreateConfigurationOptions( + string endpoint, string? password = null, Action? configurationHook = null) { - RedisCredentialsKeys credential = new RedisCredentialsKeys { Endpoint = endpoint }; - if (!string.IsNullOrEmpty(password)) - credential = credential with { Password = password }; + IRedisCredentials credential = new RedisCredentialsRaw(endpoint, password); var redis = credential.CreateConfigurationOptions(configurationHook); return redis; } @@ -53,22 +59,32 @@ public static ConfigurationOptions CreateConfigurationOptions( /// A configuration hook. /// public static ConfigurationOptions CreateConfigurationOptions( - this RedisCredentialsKeys credential, + this IRedisCredentials credential, Action? configurationHook = null) { - string endpointKey = credential.Endpoint; - string passwordKey = credential.Password; - string endpoint = Environment.GetEnvironmentVariable(endpointKey) ?? endpointKey; - string? password = Environment.GetEnvironmentVariable(passwordKey) ?? passwordKey; + var (endpoint, password) = credential switch + { + RedisCredentialsRaw raw => (raw.Endpoint, raw.Password), + RedisCredentialsEnvKeys env => ( + Environment.GetEnvironmentVariable(env.Endpoint ?? END_POINT_KEY), + Environment.GetEnvironmentVariable(env.Password ?? PASSWORD_KEY) + ), + _ => throw new InvalidOperationException(credential?.GetType()?.Name) + }; + + #region Validation + + if(string.IsNullOrEmpty(endpoint)) + throw new InvalidOperationException($"{nameof(endpoint)} is null"); - var sb = new StringBuilder(); - var writer = new StringWriter(sb); + #endregion // Validation // https://stackexchange.github.io/StackExchange.Redis/Configuration.html var configuration = ConfigurationOptions.Parse(endpoint); configuration.Password = password; if (configurationHook != null) configuration.Apply(configurationHook); + return configuration; } @@ -79,20 +95,33 @@ public static ConfigurationOptions CreateConfigurationOptions( /// /// Create REDIS client. /// - /// The endpoint. - /// The password. /// The logger. /// A configuration hook. /// public static async Task CreateProviderAsync( - string? endpoint = null, + ILogger? logger = null, + Action? configurationHook = null) + { + IRedisCredentials credential = new RedisCredentialsEnvKeys(); + var redis = await credential.CreateProviderAsync(logger, configurationHook); + return redis; + } + + /// + /// Create REDIS client. + /// + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// The logger. + /// A configuration hook. + /// + public static async Task CreateProviderAsync( + string endpoint, string? password = null, ILogger? logger = null, Action? configurationHook = null) { - RedisCredentialsKeys credential = new RedisCredentialsKeys { Endpoint = endpoint }; - if (!string.IsNullOrEmpty(password)) - credential = credential with { Password = password }; + IRedisCredentials credential = new RedisCredentialsRaw(endpoint, password); var redis = await credential.CreateProviderAsync(logger, configurationHook); return redis; } @@ -105,73 +134,25 @@ public static async Task CreateProviderAsync( /// A configuration hook. /// public static async Task CreateProviderAsync( - this RedisCredentialsKeys credential, + this IRedisCredentials credential, ILogger? logger = null, Action? configurationHook = null) { - string endpointKey = credential.Endpoint; - string passwordKey = credential.Password; - string endpoint = Environment.GetEnvironmentVariable(endpointKey) ?? endpointKey; - try { - string? password = Environment.GetEnvironmentVariable(passwordKey) ?? passwordKey; - - var sb = new StringBuilder(); - var writer = new StringWriter(sb); - // https://stackexchange.github.io/StackExchange.Redis/Configuration.html - var configuration = ConfigurationOptions.Parse(endpoint); - configuration.Password = password; - if (configurationHook != null) - configuration.Apply(configurationHook); + var configuration = credential.CreateConfigurationOptions(configurationHook); var redis = await configuration.CreateProviderAsync(logger); return redis; } catch (Exception ex) { if (logger != null) - logger.LogError(ex.FormatLazy(), "REDIS CONNECTION Setting ERROR: {endpoint}", endpoint); + logger.LogError(ex.FormatLazy(), "REDIS CONNECTION Setting ERROR"); else Console.WriteLine($"REDIS CONNECTION Setting ERROR: {ex.FormatLazy()}"); throw; } - #region Event Handlers - - void OnInternalConnError(object? sender, InternalErrorEventArgs e) - { - if (logger != null) - { - logger.LogError(e.Exception, "REDIS Connection internal failure: Failure type = {typeOfConnection}, Origin = {typeOfFailure}", - e.ConnectionType, e.Origin); - } - else - Console.WriteLine($"REDIS Connection internal failure: Failure type = {e.ConnectionType}, Origin = {e.Origin}"); - } - - void OnConnErrorMessage(object? sender, RedisErrorEventArgs e) - { - if (logger != null) - { - logger.LogWarning("REDIS Connection error: {message}", - e.Message); - } - else - Console.WriteLine($"REDIS Connection error: {e.Message}"); - } - - - void OnConnectionFailed(object? sender, ConnectionFailedEventArgs e) - { - if (logger != null) - { - logger.LogError(e.Exception, "REDIS Connection failure: Failure type = {typeOfConnection}, Failure type = {typeOfFailure}", e.ConnectionType, e.FailureType); - } - else - Console.WriteLine($"REDIS Connection failure: Failure type = {e.ConnectionType}, Failure type = {e.FailureType}"); - } - - #endregion // Event Handlers } /// @@ -179,7 +160,6 @@ void OnConnectionFailed(object? sender, ConnectionFailedEventArgs e) /// /// The logger. /// The configuration. - /// The credential's environment keys. /// /// /// @@ -205,11 +185,16 @@ public static async Task CreateProviderAsync( { // keep retry to get connection on failure cfg.AbortOnConnectFail = false; - //cfg.ConnectTimeout = 15; - //cfg.SyncTimeout = 10; - //cfg.AsyncTimeout = 10; - //cfg.DefaultDatabase = Debugger.IsAttached ? 1 : null; - }); +#pragma warning disable S125 + /* + cfg.ConnectTimeout = 15; + cfg.SyncTimeout = 10; + cfg.AsyncTimeout = 10; + cfg.DefaultDatabase = Debugger.IsAttached ? 1 : null; + */ +#pragma warning restore S125 + } +); IConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync(configuration, writer); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs index 652b71b7..561e31e4 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs @@ -4,6 +4,9 @@ using StackExchange.Redis; +#pragma warning disable S3881 // "IDisposable" should be implemented correctly +#pragma warning disable S2953 // Methods named "Dispose" should implement "IDisposable.Dispose" + namespace EventSourcing.Backbone { @@ -17,6 +20,7 @@ namespace EventSourcing.Backbone public abstract class RedisConnectionFacrotyBase : IEventSourceRedisConnectionFacroty, IDisposable, IAsyncDisposable { private const int CLOSE_DELEY_MILLISECONDS = 5000; + private static readonly IRedisCredentials _redisCredentials = new RedisCredentialsEnvKeys(); private Task _redisTask; private readonly ILogger _logger; private readonly ConfigurationOptions _configuration; @@ -35,27 +39,37 @@ public abstract class RedisConnectionFacrotyBase : IEventSourceRedisConnectionFa /// /// Create REDIS configuration options. /// - /// - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. - /// - /// - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. - /// /// The configuration hook. protected RedisConnectionFacrotyBase( ILogger logger, - string? endpoint = null, + Action? configurationHook = null) + : this(new RedisCredentialsEnvKeys(), logger, configurationHook) + { + } + + /// + /// Constructor + /// + /// The logger. + /// + /// Create REDIS configuration options. + /// + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// The configuration hook. + protected RedisConnectionFacrotyBase( + ILogger logger, + string endpoint, string? password = null, Action? configurationHook = null) + : this(new RedisCredentialsRaw(endpoint, password), logger, configurationHook) + { - _logger = logger; - _configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); - _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); } + #endregion // Overloads + /// /// Constructor /// @@ -63,9 +77,10 @@ protected RedisConnectionFacrotyBase( /// The logger. /// The configuration hook. protected RedisConnectionFacrotyBase( - RedisCredentialsKeys credential, + IRedisCredentials credential, ILogger logger, Action? configurationHook = null) + { _logger = logger; _configuration = credential.CreateConfigurationOptions(configurationHook); @@ -73,8 +88,6 @@ protected RedisConnectionFacrotyBase( } - #endregion // Overloads - /// /// Constructor /// @@ -85,8 +98,8 @@ protected RedisConnectionFacrotyBase( ConfigurationOptions? configuration) { _logger = logger; - _configuration = configuration; - _redisTask = RedisClientFactory.CreateProviderAsync(configuration, logger); + _configuration = configuration ?? _redisCredentials.CreateConfigurationOptions(); + _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); } @@ -129,7 +142,9 @@ async Task IEventSourceRedisConnectionFacroty.GetAsync() { _lastResetConnection = DateTime.Now; var cn = conn; +#pragma warning disable S1481 Task _ = Task.Delay(CLOSE_DELEY_MILLISECONDS).ContinueWith(_ => cn.CloseAsync()); +#pragma warning restore S1481 _redisTask = _configuration.CreateProviderAsync(_logger); var newConn = await _redisTask; return newConn; @@ -181,7 +196,7 @@ private void Dispose(bool disposing) /// /// Dispose /// - public void Dispose() + void IDisposable.Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); @@ -193,7 +208,7 @@ public void Dispose() /// /// if set to true [disposing]. /// - public virtual void OnDispose(bool disposing) { } + protected virtual void OnDispose(bool disposing) { } /// /// Dispose diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs similarity index 56% rename from Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs index dd0736b6..0416182a 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsKeys.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs @@ -6,17 +6,15 @@ namespace EventSourcing.Backbone /// /// Environment keys for REDIS's credentials /// - public record RedisCredentialsKeys + public record RedisCredentialsEnvKeys : IRedisCredentials { /// /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual end-point and use it. /// - public string Endpoint { get; init; } = END_POINT_KEY; + public string? Endpoint { get; init; } = END_POINT_KEY; /// /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// If the environment variable doesn't exists, It assumed that the value represent an actual password and use it. /// - public string Password { get; init; } = PASSWORD_KEY; + public string? Password { get; init; } = PASSWORD_KEY; } } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs new file mode 100644 index 00000000..b19c3659 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs @@ -0,0 +1,36 @@ +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; + + +namespace EventSourcing.Backbone +{ + /// + /// Raw keys for REDIS's credentials + /// + public record RedisCredentialsRaw: IRedisCredentials + { + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// endpoint + public RedisCredentialsRaw(string endpoint, string? password = null) + { + Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); + Password = password; + } + + #endregion // Ctor + + /// + /// The raw endpoint (not an environment variable) + /// + public string? Endpoint { get; } + /// + /// The password (not an environment variable). + /// + public string? Password { get; } + } +} diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs index 40b96e16..b7fc5552 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs @@ -21,7 +21,7 @@ public interface IConsumerSubscribeBuilder : /// /// Consumer's group name. /// - /// Name of the group. + /// Alter the default consumer's group name. /// IConsumerSubscribeBuilder Group(string consumerGroup); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs index 51f1dd0e..e8325497 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs @@ -34,7 +34,6 @@ public async Task DELETE_KEYS_TEST(string pattern) string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); IEnumerable keys = server.Keys(pattern: pattern).ToArray(); - // IEnumerable keys = server.Keys(pattern: "dev:*").ToArray(); IDatabaseAsync db = conn.GetDatabase(); var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 2c4c64d1..292d06c4 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -13,6 +13,8 @@ using Xunit; using Xunit.Abstractions; +#pragma warning disable S3881 // "IDisposable" should be implemented correctly + // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest @@ -124,7 +126,6 @@ public async Task OnSucceed_ACK_Test() .Group("CONSUMER_GROUP_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") .SubscribeEventFlowConsumer(_subscriber); - //.SubscribeEventFlow(_subscriber, "CONSUMER_GROUP_1", $"TEST {DateTime.UtcNow:HH:mm:ss}"); #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) @@ -173,7 +174,6 @@ private static CancellationToken GetCancellationToken() #region Dispose pattern - ~EndToEndExplicitTests() { Dispose(); @@ -192,27 +192,5 @@ public void Dispose() } #endregion // Dispose pattern - - /// - /// The subscriber. - /// - private class Subscriber : IEventFlow - { - private readonly IEventFlow _subscriber; - - /// - /// Initializes a new instance of the class. - /// - /// The subscriber. - public Subscriber(IEventFlow subscriber) - { - _subscriber = subscriber; - } - - ValueTask IEventFlow.Stage1Async(Person PII, string payload) => _subscriber.Stage1Async(PII, payload); - - ValueTask IEventFlow.Stage2Async(JsonElement PII, JsonElement data) => _subscriber.Stage2Async(PII, data); - } - } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index f4497daf..33291f75 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -16,6 +16,8 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +#pragma warning disable S3881 // "IDisposable" should be implemented correctly + // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest namespace EventSourcing.Backbone.Tests @@ -31,7 +33,6 @@ public class EndToEndStressTests : IDisposable private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); - private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; private const int TIMEOUT = 1000 * 30; #region Ctor @@ -179,37 +180,13 @@ private async Task ValidateLongSequenceAsync( #endregion // ValidateLongSequenceAsync - #region SendSequenceAsync - - private static async Task SendSequenceAsync(ISequenceOperations producer, string pass = "1234") - { - await producer.RegisterAsync(USER); - await producer.LoginAsync("admin", pass); - await producer.EarseAsync(4335); - } - - private static async Task SendSequenceAsync(ISequenceOperationsProducer producer, string pass = "1234") - { - EventKey r1 = await producer.RegisterAsync(USER with { Comment = null }); - EventKey r2 = await producer.LoginAsync("admin", pass); - EventKey r3 = await producer.EarseAsync(4335); - return new[] { r1, r2, r3 }; - } - private static async Task SendSequenceAsync(IProducerSequenceOperations producer, string pass = "1234") - { - EventKey r1 = await producer.RegisterAsync(USER); - EventKey r2 = await producer.LoginAsync("admin", pass); - EventKey r3 = await producer.EarseAsync(4335); - return new[] { r1, r2, r3 }; - } - - #endregion // SendSequenceAsync - #region SendLongSequenceAsync - private static async Task SendLongSequenceAsync(ISequenceOperationsProducer producer, string pass = "1234") + private static async Task SendLongSequenceAsync( + ISequenceOperationsProducer producer) { - var tasks = Enumerable.Range(1, 1500).Select(async m => await producer.SuspendAsync(m)); + var tasks = Enumerable.Range(1, 1500) + .Select(async m => await producer.SuspendAsync(m)); EventKeys[] ids = await Task.WhenAll(tasks); return ids[ids.Length - 1].First(); } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 7e9ec228..3ed7b3a5 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -21,6 +21,8 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; using static EventSourcing.Backbone.EventSourceConstants; +#pragma warning disable S3881 // "IDisposable" should be implemented correctly + // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest namespace EventSourcing.Backbone.Tests @@ -369,12 +371,15 @@ public async Task Receiver_Test() #endregion // IConsumerReceiver receiver = ... AnnouncementData? res0 = await receiver.GetByIdAsync(keys[0]); + Assert.NotNull(res0); var res1 = await receiver.GetByIdAsync(keys[1]); var hasEmail = res1.Data.TryGet("email", out string? email); + Assert.True(hasEmail); Assert.Equal("admin", email); var res2 = await receiver.GetByIdAsync(keys[2]); var hasId = res2.Data.TryGet("id", out int id); Assert.Equal(4335, id); + Assert.True(hasId); } #endregion // Receiver_Test @@ -476,6 +481,9 @@ public async Task Receiver_ChangeEnvironment_AfterBuild() var res2 = await receiver.GetByIdAsync(keys[2]); var hasId = res2.Data.TryGet("id", out int id); Assert.Equal(4335, id); + Assert.NotNull(res0); + Assert.True(hasEmail); + Assert.True(hasId); } #endregion // Receiver_ChangeEnvironment_AfterBuild @@ -521,6 +529,9 @@ public async Task Receiver_ChangeEnvironment_Test() var res2 = await receiver.GetByIdAsync(keys[2]); var hasId = res2.Data.TryGet("id", out int id); Assert.Equal(4335, id); + Assert.NotNull(res0); + Assert.True(hasEmail); + Assert.True(hasId); } #endregion // Receiver_ChangeEnvironment_Test @@ -541,7 +552,7 @@ public async Task Iterator_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); CancellationToken cancellation = GetCancellationToken(); @@ -598,7 +609,7 @@ public async Task Iterator_Cancellation_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); var cts = new CancellationTokenSource(); CancellationToken globalCancellation = GetCancellationToken(); @@ -669,7 +680,7 @@ public async Task Iterator_Json_NoMeta_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); CancellationToken cancellation = GetCancellationToken(); @@ -733,7 +744,7 @@ public async Task Iterator_Json_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); CancellationToken cancellation = GetCancellationToken(); @@ -793,7 +804,7 @@ public async Task Iterator_WithFilter_Json_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); CancellationToken cancellation = GetCancellationToken(); @@ -852,7 +863,7 @@ public async Task Iterator_MapByType_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); CancellationToken cancellation = GetCancellationToken(); @@ -898,7 +909,7 @@ public async Task Iterator_MapByType_WithExtension_Test() #endregion // ISequenceOperations producer = ... - EventKeys keys = await SendSequenceAsync(producer); + await SendSequenceAsync(producer); CancellationToken cancellation = GetCancellationToken(); @@ -928,56 +939,6 @@ public async Task Iterator_MapByType_WithExtension_Test() #endregion // Iterator_MapByType_WithExtension_Test - #region OnSucceed_ACK_Test - - [Fact(Timeout = TIMEOUT)] - public async Task OnSucceed_ACK_Test() - { - #region ISequenceOperations producer = ... - - ISequenceOperationsProducer producer = _producerBuilder - .Environment(ENV) - //.WithOptions(producerOption) - .Uri(URI) - .WithLogger(_fakeLogger) - .BuildSequenceOperationsProducer(); - - #endregion // ISequenceOperations producer = ... - - await SendSequenceAsync(producer); - - CancellationToken cancellation = GetCancellationToken(); - - #region await using IConsumerLifetime subscription = ...Subscribe(...) - - await using IConsumerLifetime subscription = _consumerBuilder - .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) - .WithCancellation(cancellation) - .Environment(ENV) - .Uri(URI) - .WithLogger(_fakeLogger) - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .Subscribe(_subscriberBridge); - - #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - - await subscription.Completion; - - #region Validation - - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) - .MustHaveHappenedOnceExactly(); - - #endregion // Validation - } - - #endregion // OnSucceed_ACK_Test - #region Until_Test [Fact(Timeout = TIMEOUT)] @@ -1097,68 +1058,6 @@ public async Task GeneratedContract_Test() #endregion // GeneratedContract_Test - #region GeneratedContract_Factory_Test - - [Fact(Timeout = TIMEOUT)] - public async Task GeneratedContract_Factory_Test() - { - #region ISequenceOperations producer1 = ... - - ISequenceOperationsProducer producer1 = _producerBuilder - //.WithOptions(producerOption) - .Environment(ENV) - .Uri(URI) - .WithLogger(_fakeLogger) - .BuildSequenceOperationsProducer(); - - #endregion // ISequenceOperations producer1 = ... - - #region ISequenceOperations producer2 = ... - - IProducerSequenceOperations producer2 = _producerBuilder - .Environment(ENV) - //.WithOptions(producerOption) - .Uri(URI) - .WithLogger(_fakeLogger) - .Build(ProducerSequenceOperationsBridgePipeline.Create); - - #endregion // ISequenceOperations producer2 = ... - - await SendSequenceAsync(producer1); - await SendSequenceAsync(producer2); - - CancellationToken cancellation = GetCancellationToken(); - - #region await using IConsumerLifetime subscription = ...Subscribe(...) - - await using IConsumerLifetime subscription = _consumerBuilder - .WithOptions(o => DefaultOptions(o, 6, AckBehavior.OnSucceed)) - .WithCancellation(cancellation) - .Environment(ENV) - .Uri(URI) - .WithLogger(_fakeLogger) - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .Subscribe(new SequenceOperationsConsumerBridge(_autoSubscriber)); - - #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - - await subscription.Completion; - - #region Validation - - A.CallTo(() => _autoSubscriber.RegisterAsync(A.Ignored)) - .MustHaveHappenedTwiceExactly(); - A.CallTo(() => _autoSubscriber.LoginAsync("admin", "1234")) - .MustHaveHappenedTwiceExactly(); - A.CallTo(() => _autoSubscriber.EarseAsync(4335)) - .MustHaveHappenedTwiceExactly(); - - #endregion // Validation - } - - #endregion // GeneratedContract_Factory_Test - #region OnSucceed_ACK_WithFailure_Test [Fact(Timeout = TIMEOUT)] @@ -1693,13 +1592,6 @@ public async Task Claim_Test() #region SendSequenceAsync - private static async Task SendSequenceAsync(ISequenceOperations producer, string pass = "1234") - { - await producer.RegisterAsync(USER); - await producer.LoginAsync("admin", pass); - await producer.EarseAsync(4335); - } - private static async Task SendSequenceAsync(ISequenceOperationsProducer producer, string pass = "1234") { EventKey r1 = await producer.RegisterAsync(USER with { Comment = null }); @@ -1717,17 +1609,6 @@ private static async Task SendSequenceAsync(IProducerSequenceOperatio #endregion // SendSequenceAsync - #region SendLongSequenceAsync - - private static async Task SendLongSequenceAsync(ISequenceOperationsProducer producer, string pass = "1234") - { - var tasks = Enumerable.Range(1, 1500).Select(async m => await producer.SuspendAsync(m)); - EventKeys[] ids = await Task.WhenAll(tasks); - return ids[ids.Length - 1].First(); - } - - #endregion // SendLongSequenceAsync - #region GetCancellationToken /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs index 153f5864..709544cf 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs @@ -45,6 +45,42 @@ public HelloWorldTests(ITestOutputHelper outputHelper) : base(outputHelper) #endregion // Ctor + #region HelloWorld_Minimal_Test + + [Fact(Timeout = TIMEOUT)] + public async Task HelloWorld_Minimal_Test() + { + IHelloProducer producer = RedisProducerBuilder.Create() + .Environment("testing") + .Uri(URI) + .BuildHelloProducer(); + + IConsumerLifetime subscription = RedisConsumerBuilder.Create() + .Environment("testing") + .Uri(URI) + .SubscribeHelloConsumer(_subscriber); + + A.CallTo(() => _subscriber.WorldAsync(5)) + .Invokes(() => subscription.DisposeAsync()); + + await producer.HelloAsync("Hi"); + await producer.WorldAsync(5); + + + await subscription.Completion; + + #region Validation + + A.CallTo(() => _subscriber.HelloAsync("Hi")) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.WorldAsync(5)) + .MustHaveHappenedOnceExactly(); + + #endregion // Validation + } + + #endregion // HelloWorld_Minimal_Test + #region HelloWorld_Test [Fact(Timeout = TIMEOUT)] @@ -88,5 +124,49 @@ public async Task HelloWorld_Test() } #endregion // HelloWorld_Test + + #region HelloWorld_Direct_Endpoint_Test + + [Fact(Timeout = TIMEOUT)] + public async Task HelloWorld_Direct_Endpoint_Test() + { + IHelloProducer producer = RedisProducerBuilder.Create("localhost:6379,localhost:6380") + .Environment("testing") + .Uri(URI) + .WithLogger(_fakeLogger) + .BuildHelloProducer(); + + var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + IConsumerLifetime subscription = RedisConsumerBuilder.Create("localhost:6379,localhost:6380") + .WithOptions(o => new ConsumerOptions + { + MaxMessages = 2, // disconnect after consuming 2 messages + AckBehavior = AckBehavior.OnSucceed + }) + .WithCancellation(cancellation.Token) + .Environment("testing") + .Uri(URI) + .WithLogger(_fakeLogger) + .Group("CONSUMER_GROUP_1") // the consumer group + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") // the name of the specific consumer + .SubscribeHelloConsumer(_subscriber); + + await producer.HelloAsync("Hi"); + await producer.WorldAsync(5); + + + await subscription.Completion; + + #region Validation + + A.CallTo(() => _subscriber.HelloAsync("Hi")) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.WorldAsync(5)) + .MustHaveHappenedOnceExactly(); + + #endregion // Validation + } + + #endregion // HelloWorld_Direct_Endpoint_Test } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index cad2f6ea..32db9734 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -59,7 +59,7 @@ public InheritanceTests( _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; - var stg = new RedisConsumerChannelSetting + RedisConsumerChannelSetting stg = new RedisConsumerChannelSetting { DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior { @@ -273,7 +273,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - await tcs.Task.WithCancellation(new CancellationTokenSource(TimeSpan.FromSeconds(20)).Token); // new Task[] { subscriptionA.Completion, subscriptionB.Completion, subscriptionAB.Completion }.WhenN(2); + await tcs.Task.WithCancellation(new CancellationTokenSource(TimeSpan.FromSeconds(20)).Token); #region Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs index 9a2ba4eb..c8d8efef 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -38,11 +38,11 @@ public class MigrationReceiverTest // : IDisposable public MigrationReceiverTest(ITestOutputHelper outputHelper) { _outputHelper = outputHelper; - var credentials = new RedisCredentialsKeys { Endpoint = TARGET_KEY }; + var credentials = new RedisCredentialsEnvKeys { Endpoint = TARGET_KEY }; _targetProducerBuilder = credentials.CreateRedisProducerBuilder() .AddVoidStrategy(); - _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsKeys { Endpoint = SOURCE_KEY }) + _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsEnvKeys { Endpoint = SOURCE_KEY }) .AddS3Strategy(new S3Options { Bucket = "event-source-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs index 0025f602..3d91ac90 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs @@ -16,6 +16,5 @@ public S3StoreStrategyStressTests(ITestOutputHelper outputHelper) : (b, logger) => b.AddS3Strategy(OPTIONS)) { } - } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs index 6c1a6f8f..0780ab80 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs @@ -21,6 +21,8 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; using static EventSourcing.Backbone.EventSourceConstants; +#pragma warning disable S3881 // "IDisposable" should be implemented correctly + // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest namespace EventSourcing.Backbone.Tests @@ -42,7 +44,7 @@ public abstract class TestsBase : IDisposable /// Initializes a new instance of the class. /// /// The output helper. - public TestsBase(ITestOutputHelper outputHelper) + protected TestsBase(ITestOutputHelper outputHelper) { _outputHelper = outputHelper; } From d4b64e897bf9994909f6f52a19605a4e794256b7 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 04:42:54 +0000 Subject: [PATCH 015/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 71f9edf6..63d9692e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.24 + 1.2.25 From 85a24f6d23365352634dc9580580c2d5978e5223 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 04:43:13 +0000 Subject: [PATCH 016/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 63d9692e..2ce8e32f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.25 + 1.2.26 @@ -44,7 +44,7 @@ - + From 36dda2b7d19adbf702ed448303547321a0f4bf3c Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 08:50:36 +0300 Subject: [PATCH 017/178] rfc: partition to rui --- .../RedisConsumerChannel.cs | 4 +- .../BlobResponse.cs | 27 +++------- .../S3Repository.cs | 4 +- .../Building/IConsumerSubscribeBuilder.cs | 52 +------------------ .../IConsumerSubscribtionHubBuilder.cs | 52 +++++++++++++++++++ .../Building/Receiver/IConsumerIterator.cs | 2 +- .../Building/Receiver/IConsumerReceiver.cs | 2 +- .../Route/IConsumerEnvironmentBuilder.cs | 4 +- .../Route/IConsumerEnvironmentOfBuilder.cs | 2 +- ...itionBuilder.cs => IConsumerUriBuilder.cs} | 6 +-- .../Builder/IConsumerPlan.cs | 6 +-- .../ConsumerMetadata.cs | 2 +- .../ConsumerOptions.cs | 3 +- .../Enums/MultiConsumerBehavior.cs | 3 +- .../ConsumerBase.EventSourceSubscriber.cs | 10 ++-- .../Builder/ConsumerBuilder.Iterator.cs | 28 +++++----- .../Builder/ConsumerBuilder.Receiver.cs | 22 ++++---- .../Builder/ConsumerBuilder.cs | 46 +++++----------- .../Builder/ConsumerPlan.cs | 22 ++++---- .../Entities/Announcement/AnnouncementData.cs | 2 +- .../Entities/Announcement/Metadata.cs | 4 +- .../Building/IProducerEnvironmentBuilder.cs | 2 +- ...itionBuilder.cs => IProducerUriBuilder.cs} | 4 +- .../IProducerOverrideEnvironmentBuilder.cs | 4 +- ...lder.cs => IProducerOverrideUriBuilder.cs} | 8 +-- .../Plan/ProducerPlan.cs | 35 ++++++------- .../Builder/ProducerBuilder.cs | 24 ++++----- .../EndToEndTests.cs | 8 +-- .../InheritanceTests.cs | 12 ++--- .../MigrationTest.cs | 8 +-- .../SerializationTests.cs | 4 +- .../Controllers/EventSourceApiController.cs | 2 +- .../Jobs/MicroDemoJob.cs | 4 +- .../RegisterEventSourceExtensions.cs | 6 +-- 34 files changed, 192 insertions(+), 232 deletions(-) create mode 100644 Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribtionHubBuilder.cs rename Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/{IConsumerPartitionBuilder.cs => IConsumerUriBuilder.cs} (68%) rename Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/{IProducerPartitionBuilder.cs => IProducerUriBuilder.cs} (71%) rename Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/{IProducerOverridePartitionBuilder.cs => IProducerOverrideUriBuilder.cs} (60%) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 8dcf9506..2e8010bd 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -156,7 +156,7 @@ public async ValueTask SubsribeAsync( try { await SubsribeToSingleAsync(plan, func, options, joinCancellation); - // TODO: [bnaya 2023-05-22] think of the api for multi stream subscription (by pattern) -> var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*").WithCancellation(cancellationToken) + // TODO: [bnaya 2023-05-22] think of the api for multi stream subscription (by partial uri * pattern) -> var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*").WithCancellation(cancellationToken) if (options.FetchUntilUnixDateOrEmpty != null) break; @@ -713,8 +713,6 @@ await db.StreamClaimAsync(plan.FullUri(), /// The plan. /// The cancellation token. /// - /// IConsumerChannelProvider.GetAsync of [{entryId}] from [{plan.Partition}->{plan.Shard}] return nothing. - /// IConsumerChannelProvider.GetAsync of [{entryId}] from [{plan.Partition}->{plan.Shard}] was expecting single result but got [{entries.Length}] results async ValueTask IConsumerChannelProvider.GetByIdAsync( EventKey entryId, IConsumerPlan plan, diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs index 77cee2dd..bfc999a3 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs @@ -17,19 +17,17 @@ private BlobResponse() { } /// Create request instance. /// /// The blob key. - /// The partition. /// The e tag. /// The content version. /// Name of the file. + /// public BlobResponse( string key, - string partition, string eTag, string contentVersion, string? fileName = null) { _key = key; - _partition = partition; _eTag = eTag; _contentVersion = contentVersion; _fileName = fileName; @@ -52,21 +50,6 @@ public string Key #endregion Key - #region Partition - - private string _partition = string.Empty; - /// - /// Gets or sets the partition. - /// - public string Partition - { - get => _partition; - [Obsolete("Exposed for the serializer", true)] - set => _partition = value; - } - - #endregion Partition - #region FileName private string? _fileName = string.Empty; @@ -138,7 +121,6 @@ public bool Equals(BlobResponse? other) return other != null && _key == other._key && _fileName == other._fileName && - _partition == other._partition && _eTag == other._eTag && _contentVersion == other._contentVersion; } @@ -151,9 +133,14 @@ public bool Equals(BlobResponse? other) /// public override int GetHashCode() { - return HashCode.Combine(_key, _fileName, _partition, _eTag, _contentVersion); + return HashCode.Combine( + _key, + _fileName, + _eTag, + _contentVersion); } + /// /// Implements the operator ==. /// diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index ee0d3655..5b894ecc 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -339,7 +339,7 @@ public async ValueTask SaveAsync( if (_dryRun) { - return new BlobResponse(key, _bucket, string.Empty, string.Empty); + return new BlobResponse(key, string.Empty, string.Empty); } #endregion // if (_dryRun) return ... @@ -355,7 +355,7 @@ public async ValueTask SaveAsync( #endregion // Validation - BlobResponse response = new BlobResponse(key, _bucket, res.ETag, res.VersionId); + BlobResponse response = new BlobResponse(key, res.ETag, res.VersionId); return response; } #region Exception Handling diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs index b7fc5552..b9ca2033 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs @@ -7,7 +7,7 @@ public interface IConsumerSubscribeBuilder : IConsumerSubscribtionHubBuilder, IConsumerEnvironmentOfBuilder, - IConsumerPartitionBuilder, + IConsumerUriBuilder, IWithCancellation //IConsumerShardOfBuilder { @@ -47,54 +47,4 @@ public interface IConsumerSubscribeBuilder : /// IConsumerIterator BuildIterator(); } - - public interface IConsumerSubscribtionHubBuilder - { - /// - /// Subscribe consumer. - /// - /// Per operation invocation handler, handle methods calls. - /// - /// Remove subscription. - /// keeping the disposable will prevent the consumer to be collected - /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - /// - IConsumerLifetime Subscribe(params ISubscriptionBridge[] handlers); - - /// - /// Subscribe consumer. - /// - /// Per operation invocation handler, handle methods calls. - /// - /// Remove subscription. - /// keeping the disposable will prevent the consumer to be collected - /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - /// - IConsumerLifetime Subscribe(IEnumerable handlers); - - /// - /// Subscribe consumer. - /// - /// Per operation invocation handler, handle methods calls. - /// - /// Remove subscription. - /// keeping the disposable will prevent the consumer to be collected - /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - /// - IConsumerLifetime Subscribe( - params Func>[] handlers); - - /// - /// Subscribe consumer. - /// - /// Per operation invocation handler, handle methods calls. - /// - /// Remove subscription. - /// keeping the disposable will prevent the consumer to be collected - /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). - /// - IConsumerLifetime Subscribe( - IEnumerable>> handlers); - - } } diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribtionHubBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribtionHubBuilder.cs new file mode 100644 index 00000000..4466d989 --- /dev/null +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribtionHubBuilder.cs @@ -0,0 +1,52 @@ +namespace EventSourcing.Backbone.Building +{ + public interface IConsumerSubscribtionHubBuilder + { + /// + /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// Remove subscription. + /// keeping the disposable will prevent the consumer to be collected + /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). + /// + IConsumerLifetime Subscribe(params ISubscriptionBridge[] handlers); + + /// + /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// Remove subscription. + /// keeping the disposable will prevent the consumer to be collected + /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). + /// + IConsumerLifetime Subscribe(IEnumerable handlers); + + /// + /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// Remove subscription. + /// keeping the disposable will prevent the consumer to be collected + /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). + /// + IConsumerLifetime Subscribe( + params Func>[] handlers); + + /// + /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// Remove subscription. + /// keeping the disposable will prevent the consumer to be collected + /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). + /// + IConsumerLifetime Subscribe( + IEnumerable>> handlers); + + } +} diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs index 9d212481..22a7701a 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs @@ -7,7 +7,7 @@ namespace EventSourcing.Backbone /// public interface IConsumerIterator : IConsumerEnvironmentOfBuilder, - IConsumerPartitionBuilder, + IConsumerUriBuilder, IConsumerIteratorCommands { /// diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs index f4aa9bb7..cd4fdc5a 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs @@ -7,7 +7,7 @@ namespace EventSourcing.Backbone /// public interface IConsumerReceiver : IConsumerEnvironmentOfBuilder, - IConsumerPartitionBuilder, + IConsumerUriBuilder, IConsumerReceiverCommands { } diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs index c0828daa..e8a34114 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs @@ -4,8 +4,8 @@ /// Event Source producer builder. /// public interface IConsumerEnvironmentBuilder : - IConsumerPartitionBuilder, - IConsumerEnvironmentOfBuilder> + IConsumerUriBuilder, + IConsumerEnvironmentOfBuilder> { } } diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs index 7a16f5ea..020ba999 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs @@ -7,7 +7,7 @@ public interface IConsumerEnvironmentOfBuilder { /// /// Include the environment as prefix of the stream key. - /// for example: production:partition-name:shard-name + /// for example: env:uri /// /// The environment (null: keep current environment, empty: reset the environment to nothing). /// diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerPartitionBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs similarity index 68% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerPartitionBuilder.cs rename to Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs index cd0f1c1b..649d6ef8 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerPartitionBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs @@ -4,13 +4,13 @@ /// /// Event Source producer builder. /// - public interface IConsumerPartitionBuilder + public interface IConsumerUriBuilder { /// /// The stream identifier (the URI combined with the environment separate one stream from another) /// - /// The partition key. + /// The URI. /// - T Uri(string partition); + T Uri(string uri); } } diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs index d19cd746..686b16d2 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs @@ -22,11 +22,11 @@ public interface IConsumerPlan : IConsumerPlanBase IConsumerPlan ChangeEnvironment(Env? environment); /// - /// change the stream's key (identity). + /// change the stream's name (identity). /// - /// The partition. + /// The URI. /// An IConsumerPlan. - IConsumerPlan ChangeKey(string? partition); + IConsumerPlan ChangeKey(string? uri); /// /// Gets the storage strategies. diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs index 86b2824e..4ae94621 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs @@ -7,7 +7,7 @@ namespace EventSourcing.Backbone /// a communication channel (Pub/Sub, Event Source, REST, GraphQL). /// It represent the operation's intent or represent event. /// - [DebuggerDisplay("{Metadata.Partition}/{Metadata.Shard} [{Metadata.MessageId} at {Metadata.ProducedAt}]")] + [DebuggerDisplay("{Metadata.Uri} [{Metadata.MessageId} at {Metadata.ProducedAt}]")] public sealed class ConsumerMetadata { internal static readonly AsyncLocal _metaContext = new AsyncLocal(); diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs index e62e53ea..9ae0fbdd 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs @@ -102,8 +102,7 @@ public DateTimeOffset? FetchUntilDateOrEmpty /// /// Represent single consuming subscription /// - private class EventSourceSubscriber : IConsumerLifetime, IConsumerBridge + private sealed class EventSourceSubscriber : IConsumerLifetime, IConsumerBridge { private readonly IConsumer _consumer; private readonly CancellationTokenSource _disposeCancellation = new CancellationTokenSource(); @@ -261,7 +261,7 @@ private async ValueTask ConsumingAsync( /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge[] handlers) @@ -275,7 +275,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge[ /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( @@ -291,7 +291,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( @@ -306,7 +306,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs index d3175d57..a1bfd82a 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs @@ -8,13 +8,11 @@ namespace EventSourcing.Backbone { public partial class ConsumerBuilder { - #region private class Iterator - /// /// Receive data (on demand data query). /// - [DebuggerDisplay("{_plan.Environment}:{_plan.Partition}:{_plan.Shard}")] - private class Iterator : IConsumerIterator + [DebuggerDisplay("{_plan.Environment}:{_plan.Uri}")] + private sealed class Iterator : IConsumerIterator { private readonly IConsumerPlan _plan; @@ -35,7 +33,7 @@ public Iterator(IConsumerPlan plan) /// /// Include the environment as prefix of the stream key. - /// for example: production:partition-name:shard-name + /// for example: env:URI /// /// The environment (null: keep current environment, empty: reset the environment to nothing). /// @@ -51,22 +49,22 @@ IConsumerIterator IConsumerEnvironmentOfBuilder.Environment(E #endregion // Environment - #region Partition + #region Uri /// - /// replace the partition of the stream key. - /// for example: production:partition-name:shard-name + /// replace the stream's key (identity). + /// for example: env:URI /// - /// The partition. + /// The URI. /// - IConsumerIterator IConsumerPartitionBuilder.Uri(string partition) + IConsumerIterator IConsumerUriBuilder.Uri(string uri) { - IConsumerPlan plan = _plan.ChangeKey(partition); + IConsumerPlan plan = _plan.ChangeKey(uri); var result = new Iterator(plan); return result; } - #endregion // Partition + #endregion // Uri #region GetAsyncEnumerable @@ -132,8 +130,8 @@ IConsumerIterator IConsumerIterator.Specialize(ICo /// /// Receive data (on demand data query). /// - [DebuggerDisplay("{_plan.Environment}:{_plan.Partition}:{_plan.Shard}")] - private class Iterator : IConsumerIterator + [DebuggerDisplay("{_plan.Environment}:{_plan.Uri}")] + private sealed class Iterator : IConsumerIterator { private readonly IConsumerPlan _plan; private readonly IConsumerIterator _iterator; @@ -222,7 +220,5 @@ async IAsyncEnumerable IConsumerIteratorCommands.GetAsyncE #endregion // GetAsyncEnumerable } - - #endregion // private class Iterator } } diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs index a06392b5..f917cb78 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs @@ -7,13 +7,12 @@ namespace EventSourcing.Backbone { public partial class ConsumerBuilder { - #region private class Receiver /// /// Receive data (on demand data query). /// - [DebuggerDisplay("{_plan.Environment}:{_plan.Partition}:{_plan.Shard}")] - private class Receiver : IConsumerReceiver + [DebuggerDisplay("{_plan.Environment}:{_plan.Uri}")] + private sealed class Receiver : IConsumerReceiver { private readonly IConsumerPlan _plan; @@ -34,7 +33,7 @@ public Receiver(IConsumerPlan plan) /// /// Include the environment as prefix of the stream key. - /// for example: production:partition-name:shard-name + /// for example: env:URI /// /// The environment (null: keep current environment, empty: reset the environment to nothing). /// @@ -50,20 +49,23 @@ IConsumerReceiver IConsumerEnvironmentOfBuilder.Environment(E #endregion // Environment + #region Uri /// - /// replace the partition of the stream key. - /// for example: production:partition-name:shard-name + /// replace the URI of the stream key. + /// for example: env:URI /// - /// The partition. + /// The URI. /// - IConsumerReceiver IConsumerPartitionBuilder.Uri(string partition) + IConsumerReceiver IConsumerUriBuilder.Uri(string uri) { - IConsumerPlan plan = _plan.ChangeKey(partition); + IConsumerPlan plan = _plan.ChangeKey(uri); var result = new Receiver(plan); return result; } + #endregion // Uri + #region GetByIdAsync /// @@ -104,7 +106,5 @@ async ValueTask IConsumerReceiverCommands.GetJsonByIdAsync( #endregion // GetJsonByIdAsync } - - #endregion // private class Receiver } } diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index 73a81149..05edcb29 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -16,7 +16,7 @@ namespace EventSourcing.Backbone /// /// Event Source consumer builder. /// - [DebuggerDisplay("{_plan.Environment}:{_plan.Partition}:{_plan.Shard}")] + [DebuggerDisplay("{_plan.Environment}:{_plan.Uri}")] public partial class ConsumerBuilder : IConsumerBuilder, IConsumerReadyBuilder, @@ -145,11 +145,11 @@ IConsumerSubscribeBuilder IConsumerSubscribeBuilder.WithOptions(Func /// Include the environment as prefix of the stream key. - /// for example: production:partition-name:shard-name + /// for example: env:URI /// /// The environment (null: keep current environment, empty: reset the environment to nothing). /// - IConsumerPartitionBuilder IConsumerEnvironmentOfBuilder>.Environment(Env? environment) + IConsumerUriBuilder IConsumerEnvironmentOfBuilder>.Environment(Env? environment) { if (environment == null) return this; @@ -161,7 +161,7 @@ IConsumerPartitionBuilder IConsumerEnvironmentOfBuilder /// Include the environment as prefix of the stream key. - /// for example: production:partition-name:shard-name + /// for example: env:URI /// /// The environment (null: keep current environment, empty: reset the environment to nothing). /// @@ -177,25 +177,16 @@ IConsumerSubscribeBuilder IConsumerEnvironmentOfBuilder - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// Stream's name /// /// /// The stream identifier (the URI combined with the environment separate one stream from another) /// /// - IConsumerReadyBuilder IConsumerPartitionBuilder.Uri(string uri) + IConsumerReadyBuilder IConsumerUriBuilder.Uri(string uri) { var prms = _plan.WithKey(uri); var result = new ConsumerBuilder(prms); @@ -203,22 +194,13 @@ IConsumerReadyBuilder IConsumerPartitionBuilder.Uri(strin } /// - /// Partition key represent logical group of - /// event source shards. - /// For example assuming each ORDERING flow can have its - /// own messaging sequence, yet can live concurrency with - /// other ORDER's sequences. - /// The partition will let consumer the option to be notify and - /// consume multiple shards from single consumer. - /// This way the consumer can handle all orders in - /// central place without affecting sequence of specific order - /// flow or limiting the throughput. + /// Stream's name /// /// /// The stream identifier (the URI combined with the environment separate one stream from another) /// /// - IConsumerSubscribeBuilder IConsumerPartitionBuilder.Uri( + IConsumerSubscribeBuilder IConsumerUriBuilder.Uri( string uri) { var prms = _plan.WithKey(uri); @@ -226,7 +208,7 @@ IConsumerSubscribeBuilder IConsumerPartitionBuilder.U return result; } - #endregion // Partition + #endregion // Uri #region RegisterSegmentationStrategy @@ -375,7 +357,7 @@ IConsumerSubscribeBuilder IConsumerSubscribeBuilder.Name(string consumerName) /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge[] handlers) @@ -389,7 +371,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge[ /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( @@ -418,7 +400,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( @@ -433,7 +415,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( /// /// Per operation invocation handler, handle methods calls. /// - /// The partition subscription (dispose to remove the subscription) + /// The subscription lifetime (dispose to remove the subscription) /// /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index 3a2eba6a..ae7b560e 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -15,7 +15,7 @@ namespace EventSourcing.Backbone /// Hold builder definitions. /// Define the consumer execution pipeline. /// - [DebuggerDisplay("{Environment}:{Partition}:{Shard}, Consumer: [{ConsumerGroup}, {ConsumerName}]")] + [DebuggerDisplay("{Environment}:{Uri}, Consumer: [{ConsumerGroup}, {ConsumerName}]")] public class ConsumerPlan : IConsumerPlan, IConsumerPlanBuilder { public static readonly ConsumerPlan Empty = new ConsumerPlan(); @@ -164,14 +164,14 @@ IConsumerChannelProvider IConsumerPlan.Channel #endregion // Environment - #region Key + #region Uri /// /// The stream's key (identity) /// public string Uri { get; } = string.Empty; - #endregion // Partition + #endregion // Uri #region Options @@ -227,7 +227,7 @@ IConsumerChannelProvider IConsumerPlan.Channel /// /// Routes are sub-pipelines are results of merge operation - /// which can split same payload into multiple partitions or shards. + /// which can split same payload into multiple URIs. /// private readonly IImmutableList Routes = ImmutableList.Empty; @@ -398,13 +398,13 @@ IConsumerPlan IConsumerPlan.ChangeEnvironment(Env? environment) /// /// change the stream's key. /// - /// The partition. + /// The URI. /// - IConsumerPlan IConsumerPlan.ChangeKey(string? partition) + IConsumerPlan IConsumerPlan.ChangeKey(string? uri) { - if (partition == null) return this; + if (uri == null) return this; - return WithKey(partition); + return WithKey(uri); } #endregion // ChangeKey @@ -414,11 +414,11 @@ IConsumerPlan IConsumerPlan.ChangeKey(string? partition) /// /// Attach a key. /// - /// The partition. + /// The URI. /// - internal ConsumerPlan WithKey(string partition) + internal ConsumerPlan WithKey(string uri) { - return new ConsumerPlan(this, key: partition); + return new ConsumerPlan(this, key: uri); } #endregion // WithKey diff --git a/EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs index c7dfba6c..24b2d2a0 100644 --- a/EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/AnnouncementData.cs @@ -6,7 +6,7 @@ namespace EventSourcing.Backbone /// Non-generics form of announcement representation, /// used to transfer data via channels. /// - [DebuggerDisplay("{Operation} [{MessageId}]: Origin:{Origin}, {Environment}->{Partition}->{Shard}, EventKey:{EventKey}")] + [DebuggerDisplay("{Operation} [{MessageId}]: Origin:{Origin}, Target:{Environment}:{Uri}, EventKey:{EventKey}")] public record AnnouncementData : Metadata { #region Data diff --git a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs index 7d6ab9ef..c707a3d4 100644 --- a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs @@ -12,7 +12,7 @@ namespace EventSourcing.Backbone /// Unlike the segments, this part can be flow with /// message & will be set as async-context.]]> /// - [DebuggerDisplay("{Operation} [{MessageId}]: Origin:{Origin}, {Environment}->{Partition}->{Shard}, EventKey:{EventKey}")] + [DebuggerDisplay("{Operation} [{MessageId}]: Origin:{Origin}, Target:{Environment}:{Uri}, EventKey:{EventKey}")] public record Metadata { #region MessageId @@ -133,7 +133,7 @@ public static class MetadataExtensions #region FullUri /// - /// Gets the partition:shard as key. + /// Gets the env:URI as key. /// public static string FullUri(this Metadata meta, char separator = ':') { diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs index d01616ac..aec185e0 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs @@ -3,7 +3,7 @@ /// /// Origin environment of the message /// - public interface IProducerEnvironmentBuilder : IProducerPartitionBuilder, IProducerBuilderEnvironment, + public interface IProducerEnvironmentBuilder : IProducerUriBuilder, IProducerBuilderEnvironment, IProducerRawBuilder { } diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerPartitionBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerUriBuilder.cs similarity index 71% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerPartitionBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerUriBuilder.cs index f82cc728..dabd1956 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerPartitionBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerUriBuilder.cs @@ -2,9 +2,9 @@ { /// - /// Partition key represent logical group + /// The stream's key (identity) /// - public interface IProducerPartitionBuilder : IProducerRawBuilder, IProducerLoggerBuilder + public interface IProducerUriBuilder : IProducerRawBuilder, IProducerLoggerBuilder { /// /// The stream key diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs index 95e4d8f3..11761db7 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs @@ -4,7 +4,7 @@ /// Enable dynamic transformation of the stream id before sending. /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. /// - public interface IProducerOverrideEnvironmentBuilder : IProducerOverridePartitionBuilder where T : class + public interface IProducerOverrideEnvironmentBuilder : IProducerOverrideUriBuilder where T : class { /// @@ -13,6 +13,6 @@ public interface IProducerOverrideEnvironmentBuilder : IProducerOverrideParti /// /// The environment. /// - IProducerOverridePartitionBuilder Environment(Env environment); + IProducerOverrideUriBuilder Environment(Env environment); } } diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverridePartitionBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideUriBuilder.cs similarity index 60% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverridePartitionBuilder.cs rename to Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideUriBuilder.cs index 57b41d37..1753d25f 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverridePartitionBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideUriBuilder.cs @@ -4,16 +4,16 @@ /// Enable dynamic transformation of the stream id before sending. /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. /// - public interface IProducerOverridePartitionBuilder : IProducerOverrideBuildBuilder where T : class + public interface IProducerOverrideUriBuilder : IProducerOverrideBuildBuilder where T : class { /// - /// Override the partition. + /// Override the URI. /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. /// - /// The partition. + /// The URI. /// The type. /// - IProducerOverrideBuildBuilder Partition(string partition, RouteAssignmentType type = RouteAssignmentType.Prefix); + IProducerOverrideBuildBuilder Uri(string uri, RouteAssignmentType type = RouteAssignmentType.Prefix); } } diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs index be9e269d..dcd797d1 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs @@ -33,7 +33,7 @@ private ProducerPlan() /// The channel. /// The channel. /// The environment. - /// The partition. + /// The URI. /// The logger. /// The options. /// The segmentation strategies. @@ -47,7 +47,7 @@ private ProducerPlan( Func? channelFactory = null, IProducerChannelProvider? channel = null, string? environment = null, - string? key = null, + string? uri = null, ILogger? logger = null, EventSourceOptions? options = null, IImmutableList? segmentationStrategies = null, @@ -60,7 +60,7 @@ private ProducerPlan( ChannelFactory = channelFactory ?? copyFrom.ChannelFactory; _channel = channel ?? copyFrom._channel; Environment = environment ?? copyFrom.Environment; - Uri = key ?? copyFrom.Uri; + Uri = uri ?? copyFrom.Uri; Options = options ?? copyFrom.Options; SegmentationStrategies = segmentationStrategies ?? copyFrom.SegmentationStrategies; Interceptors = interceptors ?? copyFrom.Interceptors; @@ -222,7 +222,7 @@ IProducerChannelProvider IProducerPlan.Channel /// /// Routes are sub-pipelines are results of merge operation - /// which can split same payload into multiple partitions or shards. + /// which can split same payload into multiple URIs. /// private readonly IImmutableList Routes = ImmutableList.Empty; @@ -293,21 +293,19 @@ public ProducerPlan WithOptions(EventSourceOptions options) /// Withes the environment. /// /// The environment. - /// The partition. - /// The shard. - /// The type (only for partition and shard). + /// The URI. + /// The type (only for uri). /// public ProducerPlan WithEnvironment( Env environment, - string? partition = null, - string? shard = null, + string? uri = null, RouteAssignmentType type = RouteAssignmentType.Replace) { return type switch { - RouteAssignmentType.Prefix => new ProducerPlan(this, environment: environment, key: $"{partition}{this.Uri}"), - RouteAssignmentType.Replace => new ProducerPlan(this, environment: environment, key: partition ?? this.Uri), - RouteAssignmentType.Suffix => new ProducerPlan(this, environment: environment, key: $"{this.Uri}{partition}"), + RouteAssignmentType.Prefix => new ProducerPlan(this, environment: environment, uri: $"{uri}{this.Uri}"), + RouteAssignmentType.Replace => new ProducerPlan(this, environment: environment, uri: uri ?? this.Uri), + RouteAssignmentType.Suffix => new ProducerPlan(this, environment: environment, uri: $"{this.Uri}{uri}"), _ => this, }; } @@ -319,18 +317,17 @@ public ProducerPlan WithEnvironment( /// /// Withes the stream's key (identifier). /// - /// The partition. - /// The shard. + /// The URI. /// The type. + /// /// - public ProducerPlan WithKey(string partition, string? shard = null, - RouteAssignmentType type = RouteAssignmentType.Replace) + public ProducerPlan WithKey(string uri, RouteAssignmentType type = RouteAssignmentType.Replace) { return type switch { - RouteAssignmentType.Prefix => new ProducerPlan(this, key: $"{partition}{this.Uri}"), - RouteAssignmentType.Replace => new ProducerPlan(this, key: partition), - RouteAssignmentType.Suffix => new ProducerPlan(this, key: $"{this.Uri}{partition}"), + RouteAssignmentType.Prefix => new ProducerPlan(this, uri: $"{uri}{this.Uri}"), + RouteAssignmentType.Replace => new ProducerPlan(this, uri: uri), + RouteAssignmentType.Suffix => new ProducerPlan(this, uri: $"{this.Uri}{uri}"), _ => this, }; } diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index d856aef2..8c94f161 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -137,7 +137,7 @@ async Task Local(ILogger logger) /// Origin environment of the message /// /// - IProducerPartitionBuilder IProducerBuilderEnvironment.Environment(Env? environment) + IProducerUriBuilder IProducerBuilderEnvironment.Environment(Env? environment) { if (environment == null) return this; @@ -167,11 +167,11 @@ IProducerSpecializeBuilder IProducerBuilderEnvironment /// The stream's key /// - /// The partition key. + /// The URI. /// - IProducerHooksBuilder IProducerPartitionBuilder.Uri(string key) + IProducerHooksBuilder IProducerUriBuilder.Uri(string uri) { - var prms = Plan.WithKey(key); + var prms = Plan.WithKey(uri); return new ProducerBuilder(prms); } @@ -312,7 +312,7 @@ IProducerSpecializeBuilder IProducerLoggerBuilder.Wi /// /// The logger. /// - IProducerPartitionBuilder IProducerLoggerBuilder.WithLogger(ILogger logger) + IProducerUriBuilder IProducerLoggerBuilder.WithLogger(ILogger logger) { return WithLogger(logger); } @@ -463,7 +463,7 @@ IProducerOverrideBuildBuilder IProducerOverrideBuilder.Strategy(Func /// The environment. /// - IProducerOverridePartitionBuilder IProducerOverrideEnvironmentBuilder.Environment(Env environment) + IProducerOverrideUriBuilder IProducerOverrideEnvironmentBuilder.Environment(Env environment) { var plan = _plan.WithEnvironment(environment ?? _plan.Environment); return new Router(plan); @@ -471,22 +471,22 @@ IProducerOverridePartitionBuilder IProducerOverrideEnvironmentBuilder.Envi #endregion // Environment - #region Partition + #region Uri /// - /// Override the partition. + /// Override the URI. /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. /// - /// The partition. + /// The URI. /// The type. /// - IProducerOverrideBuildBuilder IProducerOverridePartitionBuilder.Partition(string partition, RouteAssignmentType type) + IProducerOverrideBuildBuilder IProducerOverrideUriBuilder.Uri(string uri, RouteAssignmentType type) { - var plan = _plan.WithKey(partition, type: type); + var plan = _plan.WithKey(uri, type: type); return new Router(plan); } - #endregion // Partition + #endregion // Uri } #endregion // Router diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 3ed7b3a5..bfd52c76 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -1348,18 +1348,18 @@ public async Task Override_Test() ISequenceOperationsProducer producerPrefix = producerBuilder .Specialize() .Environment("dev") - .Partition("p0.") + .Uri("p0.") .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerPrefix1 = producerBuilder .Specialize() - .Partition("p2.").BuildSequenceOperationsProducer(); + .Uri("p2.").BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerSuffix = producerBuilder .Specialize() - .Partition(".s0", RouteAssignmentType.Suffix) + .Uri(".s0", RouteAssignmentType.Suffix) .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerSuffix1 = producerBuilder .Specialize() - .Partition(".s2", RouteAssignmentType.Suffix) + .Uri(".s2", RouteAssignmentType.Suffix) .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerDynamic = producerBuilder.Environment("Fake Env") .Specialize() diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index 32db9734..a09a9dee 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -37,7 +37,7 @@ public class InheritanceTests : IDisposable private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"Development"; - private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1_000 * 50; @@ -103,7 +103,7 @@ public async Task Inheritance_PartialConsumer_Strict_Succeed_Test(MultiConsumerB IFlowABProducer producer = _producerBuilder .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .BuildFlowABProducer(); #endregion // ISequenceOperations producer = ... @@ -140,7 +140,7 @@ public async Task Inheritance_PartialConsumer_Strict_Succeed_Test(MultiConsumerB }) .WithCancellation(cancellation) .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .Group("CONSUMER_GROUP_X_1") .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") .SubscribeFlowAConsumer(_subscriberA) @@ -188,7 +188,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() IFlowABProducer producer = _producerBuilder .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .BuildFlowABProducer(); #endregion // ISequenceOperations producer = ... @@ -251,7 +251,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() }) .WithCancellation(cancellation) .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .Group("CONSUMER_GROUP_X_1"); await using IConsumerLifetime subscriptionA = @@ -319,7 +319,7 @@ public void Dispose() configurationHook: cfg => cfg.AllowAdmin = true).Result; string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); IDatabaseAsync db = conn.GetDatabase(); var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 8c7bb991..4f4ee647 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -32,7 +32,7 @@ public class MigrationTest : IDisposable private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"Development"; - private readonly string PARTITION = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly string SHARD = $"some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); @@ -127,7 +127,7 @@ public async Task Migration_Test() ISequenceOperationsProducer producer = _producerBuilder //.WithOptions(producerOption) .Environment(ENV) - .Uri(PARTITION) + .Uri(URI) .WithLogger(_fakeLogger) .BuildSequenceOperationsProducer(); @@ -146,7 +146,7 @@ public async Task Migration_Test() var consumerBuilder = _consumerBuilder .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) - .Uri(PARTITION) + .Uri(URI) .WithLogger(_fakeLogger); #endregion // var consumerBuilder = _consumerBuilder... @@ -275,7 +275,7 @@ public void Dispose() configurationHook: cfg => cfg.AllowAdmin = true).Result; string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{PARTITION}*"); + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); IDatabaseAsync db = conn.GetDatabase(); var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); diff --git a/Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs b/Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs index c4dc8bf0..5fa12e2d 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/SerializationTests.cs @@ -13,7 +13,7 @@ public void Metadata_Serialization_Test() { MessageId = "message1", Environment = "env1", - Uri = "partition1", + Uri = "uri1", Operation = "operation1" }; @@ -55,7 +55,7 @@ public void Announcement_Serialization_Test() { MessageId = "message1", Environment = "env1", - Uri = "partition1", + Uri = "uri1", Operation = "operation1" }; meta = meta with { Linked = meta, Origin = MessageOrigin.Copy }; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs index f3678072..274b5125 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs @@ -68,7 +68,7 @@ public async ValueTask GetAsync(string eventKey, string env) /// /// /// - [HttpGet("more/{partition}/{shard}/{eventKey}/{env?}")] + [HttpGet("more/{uri}/{eventKey}/{env?}")] //[AllowAnonymous] [ProducesResponseType(StatusCodes.Status201Created)] public async ValueTask GetMoreAsync(string uri, string eventKey, string? env = null) diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs index 641178de..798e4ab4 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs @@ -83,7 +83,7 @@ async ValueTask IEventFlowConsumer.Stage1Async(Person PII, string payload) { Metadata? meta = ConsumerMetadata.Context; - _logger.LogInformation("Consume First Stage {partition} {PII} {data}", + _logger.LogInformation("Consume First Stage {uri} {PII} {data}", meta?.Uri, PII, payload); await _producer.Stage2Async( @@ -94,7 +94,7 @@ await _producer.Stage2Async( ValueTask IEventFlowConsumer.Stage2Async(JsonElement PII, JsonElement data) { var meta = ConsumerMetadata.Context; - _logger.LogInformation("Consume 2 Stage {partition} {PII} {data}", + _logger.LogInformation("Consume 2 Stage {uri} {PII} {data}", meta?.Metadata?.Uri, PII, data); return ValueTask.CompletedTask; } diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 561fef87..f5cc7967 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Configuration /// public static class RegisterEventSourceExtensions { - private const string PARTITION = "demo"; + private const string URI = "demo"; public static IServiceCollection AddEventSource( this IServiceCollection services, @@ -32,7 +32,7 @@ public static IServiceCollection AddEventSource( IEventFlowProducer producer = ProducerBuilder.Empty.GetRedisChannelService(ioc) // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .Environment(env) - .Uri(PARTITION) + .Uri(URI) .WithLogger(logger) .BuildEventFlowProducer(); return producer; @@ -48,7 +48,7 @@ public static IServiceCollection AddEventSource( OriginFilter = MessageOrigin.Original }) .Environment(env) - .Uri(PARTITION); + .Uri(URI); return consumer; }); services.AddSingleton(ioc => From 790aa1a3f03fef3373022b38764a78f668596200 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 05:51:26 +0000 Subject: [PATCH 018/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2ce8e32f..602aa3d5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.26 + 1.2.27 From d8f733e735e76b5fd7e4067a5c6895ba4953d16e Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 05:52:13 +0000 Subject: [PATCH 019/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 602aa3d5..a44b44f2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.27 + 1.2.28 @@ -44,7 +44,7 @@ - + From 5f460d9ad6c79943054c325daa397c49726d8ba3 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 09:23:16 +0300 Subject: [PATCH 020/178] rfc: EventSourcingException --- .../RedisConsumerChannel.cs | 8 ++--- .../S3Repository.cs | 8 ++--- .../ConsumerDefaultSegmentationStrategy.cs | 2 +- .../EventSourcingException.cs | 33 +++++++++++++++++++ .../EndToEndTests.cs | 6 ++-- .../MigrationTest.cs | 2 +- 6 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 EventSourcing.Backbone.Abstractions/EventSourcingException.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 2e8010bd..86b819f2 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -289,7 +289,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) if (string.IsNullOrEmpty(metaJson)) { // backward comparability - string channelType = ((string?)channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]) ?? throw new Exception(nameof(MetadataExtensions.Empty.ChannelType)); + string channelType = ((string?)channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.ChannelType)); if (channelType != CHANNEL_TYPE) { @@ -299,8 +299,8 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) continue; } - string id = ((string?)channelMeta[nameof(MetadataExtensions.Empty.MessageId)]) ?? throw new Exception(nameof(MetadataExtensions.Empty.MessageId)); - string operation = ((string?)channelMeta[nameof(MetadataExtensions.Empty.Operation)]) ?? throw new Exception(nameof(MetadataExtensions.Empty.Operation)); + string id = ((string?)channelMeta[nameof(MetadataExtensions.Empty.MessageId)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.MessageId)); + string operation = ((string?)channelMeta[nameof(MetadataExtensions.Empty.Operation)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.Operation)); long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); if (fetchUntil != null && string.Compare(fetchUntil, result.Id) < 0) @@ -318,7 +318,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) } else { - meta = JsonSerializer.Deserialize(metaJson, EventSourceOptions.FullSerializerOptions) ?? throw new Exception(nameof(Metadata)); + meta = JsonSerializer.Deserialize(metaJson, EventSourceOptions.FullSerializerOptions) ?? throw new EventSourcingException(nameof(Metadata)); meta = meta with { EventKey = eventKey }; } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index 5b894ecc..71308216 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -154,7 +154,7 @@ public async ValueTask GetAsync(Env env, string id, CancellationToken canc if (response == null) { - throw new NullReferenceException("Failed to deserialize industries"); + throw new EventSourcingException("Failed to deserialize industries"); } #endregion // Validation @@ -205,12 +205,12 @@ public async ValueTask GetStreamAsync(Env env, string id, CancellationTo if (res == null) { - throw new Exception($"S3 key [{key}] not found. bucket = {bucketName}"); + throw new EventSourcingException($"S3 key [{key}] not found. bucket = {bucketName}"); } if (res.HttpStatusCode >= HttpStatusCode.Ambiguous) { - throw new Exception($"Failed to get blob [{res.HttpStatusCode}]"); + throw new EventSourcingException($"Failed to get blob [{res.HttpStatusCode}]"); } #endregion // Validation @@ -350,7 +350,7 @@ public async ValueTask SaveAsync( if (res.HttpStatusCode >= HttpStatusCode.Ambiguous) { - throw new Exception($"Failed to save blob [{res.HttpStatusCode}]"); + throw new EventSourcingException($"Failed to save blob [{res.HttpStatusCode}]"); } #endregion // Validation diff --git a/Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs index 37cd751c..3247232f 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/ConsumerDefaultSegmentationStrategy.cs @@ -36,7 +36,7 @@ public class ConsumerDefaultSegmentationStrategy : catch (Exception ex) { - throw new Exception($"Fail to serialize event [{metadata}]: operation=[{operation}], argument-name=[{argumentName}], Target type=[{typeof(T).Name}], Base64 Data=[{Convert.ToBase64String(data.ToArray())}]", ex); + throw new EventSourcingException($"Fail to serialize event [{metadata}]: operation=[{operation}], argument-name=[{argumentName}], Target type=[{typeof(T).Name}], Base64 Data=[{Convert.ToBase64String(data.ToArray())}]", ex); } #endregion // Exception Handling diff --git a/EventSourcing.Backbone.Abstractions/EventSourcingException.cs b/EventSourcing.Backbone.Abstractions/EventSourcingException.cs new file mode 100644 index 00000000..68b91513 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/EventSourcingException.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace EventSourcing.Backbone +{ + /// + /// Event sourcing exception + /// + /// + [Serializable] + public class EventSourcingException : Exception + { + public EventSourcingException() : base() + { + } + + public EventSourcingException(string? message) : base(message) + { + } + + public EventSourcingException(string? message, Exception? innerException) : base(message, innerException) + { + } + + protected EventSourcingException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index bfd52c76..4eb9d890 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -85,7 +85,7 @@ public EndToEndTests( { Metadata meta = ConsumerMetadata.Context; if (string.IsNullOrEmpty(meta.EventKey)) - return ValueTask.FromException(new Exception("Event Key is missing")); + return ValueTask.FromException(new EventSourcingException("Event Key is missing")); return ValueTask.CompletedTask; }); A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) @@ -641,7 +641,7 @@ public async Task Iterator_Cancellation_Test() { throw new OperationCanceledException("Should have been canceled"); } - else throw new Exception("Should have been canceled"); + else throw new EventSourcingException("Should have been canceled"); i++; } @@ -840,7 +840,7 @@ public async Task Iterator_WithFilter_Json_Test() var pj2 = json.GetProperty("id"); Assert.Equal(4335, pj2.GetInt32()); } - else throw new Exception("Should have been filtered"); + else throw new EventSourcingException("Should have been filtered"); i++; } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 4f4ee647..313a06ef 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -65,7 +65,7 @@ public MigrationTest( { Metadata meta = ConsumerMetadata.Context; if (string.IsNullOrEmpty(meta.EventKey)) - return ValueTask.FromException(new Exception("Event Key is missing")); + return ValueTask.FromException(new EventSourcingException("Event Key is missing")); return ValueTask.CompletedTask; }); A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) From 15c127987f978ddbd040955301442245519eaf4d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 09:26:31 +0300 Subject: [PATCH 021/178] rfc: naming --- .../RedisConsumerChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 86b819f2..a33721bf 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -827,7 +827,7 @@ async Task FindAsync(EventKey entryId) catch (Exception ex) { string key = plan.FullUri(); - _logger.LogError(ex.FormatLazy(), "{mtd} Failed: Entry [{entryId}] from [{key}] event stream", + _logger.LogError(ex.FormatLazy(), "{method} Failed: Entry [{entryId}] from [{key}] event stream", mtdName, entryId, key); throw; } From eaa4e725a065591a94019f50c8d86ca579b3a319 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 06:27:05 +0000 Subject: [PATCH 022/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index a44b44f2..a2122c6e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.28 + 1.2.29 From a0653943621dcc5b610b48d492ae24ebf15ab367 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 06:27:21 +0000 Subject: [PATCH 023/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a2122c6e..ede7670f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.29 + 1.2.30 @@ -44,7 +44,7 @@ - + From 734279d3e3cc036cdc18e03f240dafe2c3ef3fbe Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 09:35:45 +0300 Subject: [PATCH 024/178] Update README.md --- README.md | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 44bdf7a3..cd0259bf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,31 @@ -# Event-Source-Backbone [![Deploy](https://github.com/weknow-network/Event-Source-Backbone/actions/workflows/build-publish-v2.yml/badge.svg)](https://github.com/weknow-network/Event-Source-Backbone/actions/workflows/build-publish-v2.yml) [![Source Generator](https://github.com/weknow-network/Event-Source-Backbone/actions/workflows/src-gen.yml/badge.svg)](https://github.com/weknow-network/Event-Source-Backbone/actions/workflows/src-gen.yml) -[![NuGet](https://img.shields.io/nuget/v/Weknow.EventSource.Backbone.Contracts.svg)](https://www.nuget.org/packages/Weknow.EventSource.Backbone.Contracts/) +# Event-Source-Backbone -Event-Source-Backbone is a framework for working with Event-Source under .NET (6.x). +## Understanding Event Sourcing +Event sourcing is an architectural pattern that captures and persists every change as a sequence of events. It provides a historical log of events that can be used to reconstruct the current state of an application at any given point in time. This approach offers various benefits, such as auditability, scalability, and the ability to build complex workflows. -!! This Repo is still in **Alpha**, which means that its API still not stable (expecting breaking compatibility). +Event sourcing, when combined with the Command Query Responsibility Segregation (CQRS) pattern, offers even more advantages. CQRS separates the read and write concerns of an application, enabling the generation of dedicated databases optimized for specific read or write needs. This separation of concerns allows for more agile and flexible database schema designs, as they are less critical to set up in advance. -[Documentation](https://github.com/weknow-network/Event-Source-Backbone/wiki/Home---Event-Source-Backbone) +By leveraging EventSource.Backbone, developers can implement event sourcing and CQRS together, resulting in a powerful architecture that promotes scalability, flexibility, and maintainability. + +## Introducing EventSource.Backbone +One notable aspect of EventSource.Backbone is its unique approach to event sourcing. Instead of inventing a new event source database, EventSource.Backbone leverages a combination of existing message streams like Kafka, Redis Stream, or similar technologies, along with key-value databases or services like Redis. + +This architecture enables several benefits. Message streams, while excellent for handling event sequences and ensuring reliable message delivery, may not be optimal for heavy payloads. By combining key-value databases with message streams, EventSource.Backbone allows the message payload to be stored in the key-value database while the stream holds the sequence and metadata. This approach improves performance and facilitates compliance with GDPR standards by allowing the splitting of messages into different keys based on a standardized key format. + +It's worth noting that EventSource.Backbone currently provides a software development kit (SDK) for the .NET ecosystem. While the framework may expand to other programming languages and frameworks in the future, at present, it is specifically focused on the .NET platform. + +## Leveraging Existing Infrastructure +One of the major advantages of EventSource.Backbone is its compatibility with widely adopted message streaming platforms like Kafka or Redis Stream. These platforms provide robust message delivery guarantees and high throughput, making them ideal for handling event streams at scale. + +Furthermore, EventSource.Backbone seamlessly integrates with popular key-value databases such as Redis, Couchbase, or Amazon DynamoDB. This integration allows developers to leverage the strengths of these databases for efficient storage and retrieval of auxiliary data related to events. + +## Conclusion +In this introductory post, we've explored the concept of event sourcing and introduced the unique aspects of EventSource.Backbone. This framework stands out by leveraging a combination of message streams and key-value databases to achieve better performance, support GDPR standards, and provide flexibility in event sourcing implementations. + +EventSource.Backbone integrates seamlessly with popular message streaming platforms and key-value databases, enabling developers to leverage their strengths for scalable and efficient event sourcing. + +When combined with CQRS, event sourcing can enhance database schema design, making it more agile and flexible, and enabling the generation of dedicated databases optimized for specific needs. + +Stay tuned for more exciting content on event sourcing and EventSource.Backbone! If you have any questions or topics you'd like me to cover, feel free to leave a comment below. + +Feel free to further customize this blog post to align with your writing style and any additional information you wish to provide. Happy blogging! From cde0a376122df7b704064699790898fbe32724c1 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 06:36:01 +0000 Subject: [PATCH 025/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index ede7670f..e1b7cf29 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.30 + 1.2.31 From 998588f26b4c13f45c89cff99de56253c65da1b1 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 06:36:19 +0000 Subject: [PATCH 026/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index e1b7cf29..d0a2c891 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.31 + 1.2.32 @@ -44,7 +44,7 @@ - + From fd97d85c204810b1c0e0859f6c1bc49f2d6153a1 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 11:47:46 +0300 Subject: [PATCH 027/178] doc: readme --- ...bone.Channels.RedisConsumerProvider.csproj | 13 +++++- ...bone.Channels.RedisProducerProvider.csproj | 13 +++++- .../RedisProducerChannel.cs | 15 ------- ...kbone.Channels.RedisProvider.Common.csproj | 11 +++++ ...ne.Channels.S3StoreConsumerProvider.csproj | 13 +++++- ...ne.Channels.S3StoreProducerProvider.csproj | 14 ++++++- ...one.Channels.S3StoreProvider.Common.csproj | 11 +++++ .../Builder/Building/IConsumerHooksBuilder.cs | 33 --------------- .../Route/IConsumerEnvironmentOfBuilder.cs | 4 +- .../Building/Route/IConsumerUriBuilder.cs | 3 +- .../ConsumerOptions.cs | 2 - ...ing.Backbone.Consumers.Abstractions.csproj | 14 ++++++- .../ConsumerBase.EventSourceSubscriber.cs | 10 ++--- .../Builder/ConsumerBuilder.cs | 34 ++++++++------- .../Builder/ConsumerPlan.cs | 5 ++- .../EventSourcing.Backbone.Consumers.csproj | 13 +++++- .../Consumer}/IConsumerBridge.cs | 0 .../Consumer}/ISubscriptionBridge.cs | 2 - .../EventSourceOptions.cs | 21 ---------- ...EventSourcing.Backbone.Abstractions.csproj | 42 ++++++++++++------- EventSourcing.Backbone.sln | 1 + .../EventSourcing.Backbone.csproj | 34 +++++++++------ .../Building/IProducerEnvironmentBuilder.cs | 3 +- .../Builder/Building/IRawProducer.cs | 38 ++++++++++++++++- ...ing.Backbone.Producers.Abstractions.csproj | 13 +++++- .../Builder/ProducerBuilder.cs | 12 +++--- .../EventSourcing.Backbone.Producers.csproj | 13 +++++- .../MigrationTest.cs | 26 ++---------- .../SourceMigrationTests.cs | 22 +--------- .../EventSourcing.Backbone.SrcGen.csproj | 11 +++++ 30 files changed, 264 insertions(+), 182 deletions(-) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/IConsumerBridge.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/ISubscriptionBridge.cs (92%) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj index 3cfb7bf0..7bd16756 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj @@ -1,5 +1,16 @@  - + + README.md + + + + + True + \ + + + + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj index 1f75e7cd..772d4b76 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj @@ -1,6 +1,17 @@  - + + README.md + + + + + True + \ + + + + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 5aa91799..7c4d2ddc 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -47,21 +47,6 @@ public RedisProducerChannel( #endregion // Ctor - #region GetDB - - /// - /// Gets the database. - /// - /// The redis connection factory. - /// - private static async Task GetDB(IEventSourceRedisConnectionFacroty redisConnFactory) - { - var mp = await redisConnFactory.GetAsync(); - return mp.GetDatabase(); - } - - #endregion // GetDB - #region SendAsync /// diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj index 63dacac8..35d76463 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj @@ -1,5 +1,16 @@  + + README.md + + + + + True + \ + + + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj index 494e59d6..b57fbdb6 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj @@ -1,6 +1,17 @@  - + + README.md + + + + + True + \ + + + + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj index 98a7414c..de25c04d 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj @@ -1,5 +1,17 @@  - + + + README.md + + + + + True + \ + + + + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index 5eab683f..948ba2c3 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -1,5 +1,16 @@  + + README.md + + + + + True + \ + + + diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs index 27a03650..d763f4fe 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs @@ -59,38 +59,5 @@ IConsumerHooksBuilder RegisterSegmentationStrategy( /// IConsumerHooksBuilder RegisterSegmentationStrategy( IConsumerAsyncSegmentationStrategy segmentationStrategy); - - //[Consumer(Partition="X", Shard="y")] - //public class ConsumerX : Consumer, ISequenceOperations - //{ - //} - //// void BuildAutoDiscover(); - //void Build(Func factory, string partition, string? shard = null); - //IEventSourceConsumer3Builder ForType( - // IConsumerSegmentationProvider segmentationProvider, - // params string[] intents); - - //IEventSourceConsumer3Builder ForType( - // Func>, - // IDataSerializer, - // T> segmentationProvider, - // params string[] intents); - - //IEventSourceConsumer3Builder ForType( - // IConsumerSegmentationProvider segmentationProvider, - // Func filter); - - //IEventSourceConsumer3Builder ForType( - // Func>, - // IDataSerializer, - // T> segmentationProvider, - // Func filter); - - ///// - ///// Builds consumer for non-specialized announcements. - ///// This is perfect for scenarios like storing backups in blobs like S3. - ///// - ///// - //ISourceBlock> BuildRaw(); } } diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs index 020ba999..3768ed15 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs @@ -3,11 +3,11 @@ /// /// Event Source producer builder. /// - public interface IConsumerEnvironmentOfBuilder + public interface IConsumerEnvironmentOfBuilder { /// /// Include the environment as prefix of the stream key. - /// for example: env:uri + /// for example: env:URI /// /// The environment (null: keep current environment, empty: reset the environment to nothing). /// diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs index 649d6ef8..223b4fcb 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs @@ -4,7 +4,8 @@ /// /// Event Source producer builder. /// - public interface IConsumerUriBuilder + /// + public interface IConsumerUriBuilder { /// /// The stream identifier (the URI combined with the environment separate one stream from another) diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs index 9ae0fbdd..1dba4a09 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs @@ -5,8 +5,6 @@ namespace EventSourcing.Backbone public record ConsumerOptions : EventSourceOptions { - //public new static readonly ConsumerOptions Empty = new ConsumerOptions(); - #region BatchSize /// diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj index 66f56dea..5b0929b7 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj +++ b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj @@ -4,7 +4,19 @@ EventSourcing.Backbone - + + + README.md + + + + + True + \ + + + + diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index 422d0ea6..cb7adb8c 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -126,7 +126,7 @@ private async ValueTask ConsumingAsync( if (_maxMessages != 0 && _maxMessages < count) { await DisposeAsync(); - throw new OperationCanceledException(); // make sure it not auto ack; + throw new OperationCanceledException(); } #endregion // Increment & Validation Max Messages Limit @@ -152,7 +152,6 @@ private async ValueTask ConsumingAsync( #endregion // _plan.Interceptors.InterceptAsync(...) - string operation = meta.Operation; PartialConsumerBehavior partialBehavior = _options.PartialBehavior; bool hasProcessed = false; @@ -231,12 +230,9 @@ private async ValueTask ConsumingAsync( finally { - if (Plan.Options.AckBehavior == AckBehavior.OnFinally) + if (Plan.Options.AckBehavior == AckBehavior.OnFinally && partialBehavior != PartialConsumerBehavior.Sequential) { - if (partialBehavior != PartialConsumerBehavior.Sequential) - { - await ack.AckAsync(); - } + await ack.AckAsync(); } #region Validation Max Messages Limit diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index 05edcb29..fa43dc16 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -262,12 +262,12 @@ IConsumerHooksBuilder IConsumerHooksBuilder.RegisterSegmentationStrategy(IConsum /// /// Registers the interceptor. /// - /// The interceptor. + /// The interceptor. /// IConsumerHooksBuilder IConsumerHooksBuilder.RegisterInterceptor( - IConsumerInterceptor interceptor) + IConsumerInterceptor interceptorData) { - var bridge = new ConsumerInterceptorBridge(interceptor); + var bridge = new ConsumerInterceptorBridge(interceptorData); var prms = _plan.AddInterceptor(bridge); var result = new ConsumerBuilder(prms); return result; @@ -276,12 +276,12 @@ IConsumerHooksBuilder IConsumerHooksBuilder.RegisterInterceptor( /// /// Registers the interceptor. /// - /// The interceptor. + /// The interceptor. /// IConsumerHooksBuilder IConsumerHooksBuilder.RegisterInterceptor( - IConsumerAsyncInterceptor interceptor) + IConsumerAsyncInterceptor interceptorData) { - var prms = _plan.AddInterceptor(interceptor); + var prms = _plan.AddInterceptor(interceptorData); var result = new ConsumerBuilder(prms); return result; } @@ -359,7 +359,6 @@ IConsumerSubscribeBuilder IConsumerSubscribeBuilder.Name(string consumerName) /// /// The subscription lifetime (dispose to remove the subscription) /// - /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge[] handlers) { @@ -373,7 +372,6 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge[ /// /// The subscription lifetime (dispose to remove the subscription) /// - /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( IEnumerable handlers) @@ -381,7 +379,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( #region Validation if (_plan == null) - throw new ArgumentNullException(nameof(_plan)); + throw new EventSourcingException(nameof(_plan)); #endregion // Validation @@ -402,7 +400,6 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( /// /// The subscription lifetime (dispose to remove the subscription) /// - /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( params Func>[] handlers) @@ -417,7 +414,6 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( /// /// The subscription lifetime (dispose to remove the subscription) /// - /// _plan IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( IEnumerable>> handlers) @@ -425,7 +421,7 @@ IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe( #region Validation if (_plan == null) - throw new ArgumentNullException(nameof(_plan)); + throw new EventSourcingException(nameof(_plan)); #endregion // Validation @@ -572,7 +568,10 @@ private static JsonElement ToJson( { encoded = Encoding.UTF8.GetString(val.Span); } - catch { } + catch + { + plan.Logger.LogDebug("Failed to encode value of ({key})", key); + } var err = $"GetJsonByIdAsync [{entryId}, {announcement.FullUri()}]: failed to deserialize key='{key}', base64='{Convert.ToBase64String(val.ToArray())}', data={encoded}"; plan.Logger.LogError(ex.FormatLazy(), "GetJsonByIdAsync [{id}, {at}]: failed to deserialize key='{key}', base64='{value}', data={data}", @@ -639,7 +638,14 @@ private static JsonElement ToJson( { encoded = Encoding.UTF8.GetString(val.Span); } - catch { } + #region Exception Handling + + catch + { + plan.Logger.LogDebug("Encoding failure"); + } + + #endregion // Exception Handling var err = $"GetJsonByIdAsync [{entryId}, {announcement.Metadata.FullUri()}]: failed to deserialize key='{key}', base64='{Convert.ToBase64String(val.ToArray())}', data={encoded}"; plan.Logger.LogError(ex.FormatLazy(), "GetJsonByIdAsync [{id}, {at}]: failed to deserialize key='{key}', base64='{value}', data={data}", diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index ae7b560e..ab48a4fa 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -120,8 +120,11 @@ IConsumerChannelProvider IConsumerPlan.Channel { get { +#pragma warning disable S2372 // Exceptions should not be thrown from property getters if (_channel == null) - throw new ArgumentNullException("Event Source Consumer channel not set"); + throw new EventSourcingException("Event Source Consumer channel not set"); +#pragma warning restore S2372 + return _channel; } } diff --git a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj index 4fc36e51..704aeb1b 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj +++ b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj @@ -1,6 +1,17 @@  - + + README.md + + + + + True + \ + + + + diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerBridge.cs b/EventSourcing.Backbone.Abstractions/Consumer/IConsumerBridge.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerBridge.cs rename to EventSourcing.Backbone.Abstractions/Consumer/IConsumerBridge.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ISubscriptionBridge.cs b/EventSourcing.Backbone.Abstractions/Consumer/ISubscriptionBridge.cs similarity index 92% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/ISubscriptionBridge.cs rename to EventSourcing.Backbone.Abstractions/Consumer/ISubscriptionBridge.cs index 4855af73..74bd8718 100644 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ISubscriptionBridge.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/ISubscriptionBridge.cs @@ -5,8 +5,6 @@ /// public interface ISubscriptionBridge { - // TODO: [bnaya 2021-10] version filtering - /// /// Bridges to the subscriber implementation. /// diff --git a/EventSourcing.Backbone.Abstractions/EventSourceOptions.cs b/EventSourcing.Backbone.Abstractions/EventSourceOptions.cs index c42b8aaf..be65fc7e 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourceOptions.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceOptions.cs @@ -7,26 +7,10 @@ public record EventSourceOptions { private static readonly JsonStringEnumConverter EnumConvertor = new JsonStringEnumConverter(JsonNamingPolicy.CamelCase); - //private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions - //{ - // PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - // DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - // // PropertyNameCaseInsensitive = true, - // // IgnoreNullValues = true, - // //WriteIndented = true, - // Converters = - // { - // EnumConvertor, - // JsonDictionaryConverter.Default, - // JsonImmutableDictionaryConverter.Default - // } internal static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - // PropertyNameCaseInsensitive = true, - // IgnoreNullValues = true, - //WriteIndented = true, Converters = { EnumConvertor, @@ -38,9 +22,6 @@ public record EventSourceOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - // PropertyNameCaseInsensitive = true, - // IgnoreNullValues = true, - //WriteIndented = true, Converters = { EnumConvertor, @@ -51,8 +32,6 @@ public record EventSourceOptions private static readonly IDataSerializer DEFAULT_SERIALIZER = new JsonDataSerializer(FullSerializerOptions); - //public static readonly EventSourceOptions Empty = new EventSourceOptions(); - /// /// Gets the serializer. /// diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index 776e55d3..571b322d 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -1,19 +1,33 @@  - - EventSourcing.Backbone - + + EventSourcing.Backbone + https://github.com/bnayae/Event-Source-Backbone + + + + README.md + + + + + True + \ + + + + + + + + + + + + + + + - - - - - - - - - - - diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index bb9f0832..97b7b86a 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -45,6 +45,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig Directory.Build.props = Directory.Build.props + README.md = README.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "S3", "S3", "{1B19D36E-8348-4CAE-A2B8-87A81076539A}" diff --git a/EventSourcing.Backbone/EventSourcing.Backbone.csproj b/EventSourcing.Backbone/EventSourcing.Backbone.csproj index f454acf1..e31dfeaf 100644 --- a/EventSourcing.Backbone/EventSourcing.Backbone.csproj +++ b/EventSourcing.Backbone/EventSourcing.Backbone.csproj @@ -1,19 +1,29 @@  - - - + + + + + README.md + - - - - - - - - - + + + True + \ + + + + + + + + + + + + diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs index aec185e0..509adc8f 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs @@ -3,8 +3,7 @@ /// /// Origin environment of the message /// - public interface IProducerEnvironmentBuilder : IProducerUriBuilder, IProducerBuilderEnvironment, - IProducerRawBuilder + public interface IProducerEnvironmentBuilder : IProducerUriBuilder, IProducerBuilderEnvironment { } } diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs index 78e054aa..2c4d18bf 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs @@ -1,4 +1,6 @@ -namespace EventSourcing.Backbone +using System.Runtime.CompilerServices; + +namespace EventSourcing.Backbone { /// /// Event Source raw producer. @@ -11,5 +13,39 @@ public interface IRawProducer /// /// ValueTask Produce(Announcement data); + + /// + /// Converts to a subscription bridge which will forward the data into the producer when attached to a subscriber. + /// + /// + public ISubscriptionBridge ToSubscriptionBridge() + { + return new SubscriptionBridge(this); + } + + #region class SubscriptionBridge : ISubscriptionBridge ... + + /// + /// Subscription Bridge which produce forward data + /// + /// + private sealed class SubscriptionBridge : ISubscriptionBridge + { + private readonly IRawProducer _fw; + + public SubscriptionBridge(IRawProducer fw) + { + _fw = fw; + } + + public async Task BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) + { + await _fw.Produce(announcement); + return true; + + } + } + + #endregion // class SubscriptionBridge : ISubscriptionBridge ... } } diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj index 7b7b7696..183f09da 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj +++ b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj @@ -1,6 +1,17 @@  - + + README.md + + + + + True + \ + + + + diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index 8c94f161..54445f43 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -69,7 +69,7 @@ IProducerHooksBuilder IProducerBuilder.Merge( #region Validation if (Plan == null) - throw new ArgumentNullException(nameof(Plan)); + throw new EventSourcingException($"{nameof(Plan)} is null"); #endregion // Validation @@ -351,7 +351,9 @@ T IProducerSpecializeBuilder.Build(Func factory) /// /// + /// Useful for data migration at the raw data level. + /// This producer can be attached to a subscriber and route it data elesewhere. + /// For example, alternative cloud provider, qa environement, etc.]]> /// /// /// @@ -395,7 +397,7 @@ IRawProducer IProducerRawBuilder.BuildRaw(RawProducerOptions? options) /// Can use for scenario like routing between environment like dev vs. prod or aws vs azure. /// /// - private class Router : IProducerOverrideBuilder where T : class + private sealed class Router : IProducerOverrideBuilder where T : class { private readonly ProducerPlan _plan; @@ -497,7 +499,7 @@ IProducerOverrideBuildBuilder IProducerOverrideUriBuilder.Uri(string uri, /// Raw producer (useful for cluster migration) /// /// - private class RawProducer : IRawProducer + private sealed class RawProducer : IRawProducer { private readonly IProducerPlan _plan; private readonly RawProducerOptions? _options; @@ -528,7 +530,7 @@ public async ValueTask Produce(Announcement data) var strategies = await _plan.StorageStrategiesAsync; Metadata metadata = data.Metadata; Metadata meta = metadata; - if ((_options?.KeepOriginalMeta ?? false) == false) + if (!(_options?.KeepOriginalMeta ?? false)) { meta = meta with { diff --git a/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj b/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj index 87cd1b44..53e47b52 100644 --- a/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj +++ b/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj @@ -1,6 +1,17 @@  - + + README.md + + + + + True + \ + + + + diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 313a06ef..1ccd59ff 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -10,6 +10,8 @@ using Microsoft.Extensions.Logging; +using Polly; + using StackExchange.Redis; using Xunit; @@ -179,8 +181,9 @@ public async Task Migration_Test() IRawProducer fwProducer = _producerBuilder .Environment(MIGRATE_TO) .BuildRaw(); + // attach the producer into a subscription bridge - SubscriptionBridge fwSubscriberBridge = new(fwProducer); + ISubscriptionBridge fwSubscriberBridge = fwProducer.ToSubscriptionBridge(); // attach the forward subscription into a concrete stream await using IConsumerLifetime fwSubscription = consumerBuilder @@ -327,26 +330,5 @@ async Task LocalAsync(string k) } #endregion // Dispose pattern - - #region SubscriptionBridge - - private class SubscriptionBridge : ISubscriptionBridge - { - private readonly IRawProducer _fw; - - public SubscriptionBridge(IRawProducer fw) - { - _fw = fw; - } - - public async Task BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) - { - await _fw.Produce(announcement); - return true; - - } - } - - #endregion // SubscriptionBridge } } diff --git a/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs b/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs index 5fcacb1e..c1a7a317 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/SourceMigrationTests.cs @@ -69,7 +69,7 @@ public async Task Migration_Simple_Test() _producerBuilder.UseChannel(_rawProducerChannel) .BuildRaw(); - ISubscriptionBridge subscriptionBridge = new SubscriptionBridge(rawProducer); + ISubscriptionBridge subscriptionBridge = rawProducer.ToSubscriptionBridge(); var cts = new CancellationTokenSource(); @@ -141,7 +141,7 @@ public async Task Migration_Change_Target_Test() .Uri("Man:Socks") .BuildRaw(); - ISubscriptionBridge subscriptionBridge = new SubscriptionBridge(rawProducer); + ISubscriptionBridge subscriptionBridge = rawProducer.ToSubscriptionBridge(); var cts = new CancellationTokenSource(); @@ -194,23 +194,5 @@ public async Task Migration_Change_Target_Test() } #endregion // Migration_Change_Target_Test - - - private class SubscriptionBridge : ISubscriptionBridge - { - private readonly IRawProducer _fw; - - public SubscriptionBridge(IRawProducer fw) - { - _fw = fw; - } - - public async Task BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) - { - await _fw.Produce(announcement); - return true; - - } - } } } diff --git a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj index 22f2d3b8..4dc9f3eb 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj +++ b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj @@ -11,6 +11,17 @@ Debug;Release;Gen + + README.md + + + + + True + \ + + + all From e475a7b79157749f8e15b18cfbd6a77d07f10167 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 08:48:18 +0000 Subject: [PATCH 028/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index d0a2c891..30b5f971 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.32 + 1.2.33 From 046a0d7485f44dd4fda8f7948a1c0b50d979d000 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 08:48:34 +0000 Subject: [PATCH 029/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 30b5f971..5f4c195f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.33 + 1.2.34 @@ -44,7 +44,7 @@ - + From df8ebc23b6fefb82b2943a8f15e29e10eec01a5d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 14:59:28 +0300 Subject: [PATCH 030/178] rfc: GenerateEventSourceAttribute --- .../GenerateEventSourceAttribute.cs | 29 +++++++++++--- .../GenerateEventSourceBaseAttribute.cs | 38 ------------------- .../GenerateEventSourceBridgeAttribute.cs | 13 ------- 3 files changed, 24 insertions(+), 56 deletions(-) delete mode 100644 EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs delete mode 100644 EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs diff --git a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs index e1b35910..95f922b5 100644 --- a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs @@ -1,18 +1,37 @@ -namespace EventSourcing.Backbone +using System.ComponentModel; + +namespace EventSourcing.Backbone { /// /// Mark for code generation /// - /// + /// [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] - public class GenerateEventSourceAttribute : GenerateEventSourceBaseAttribute + public class GenerateEventSourceAttribute : Attribute { /// - /// Initializes a new instance of the class. + /// Initializes a new instance. /// /// Type of the generate. - public GenerateEventSourceAttribute(EventSourceGenType generateType) : base(generateType) + public GenerateEventSourceAttribute(EventSourceGenType generateType) { + Type = generateType; } + /// + /// The name of the interface. + /// If missing the generator will use a convention. + /// + public string? Name { get; init; } + + /// + /// The Namespace. + /// If missing the generator will use a convention. + /// + public string? Namespace { get; init; } + + /// + /// Type of the generation + /// + public EventSourceGenType Type { get; } } } diff --git a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs deleted file mode 100644 index c2ee44ca..00000000 --- a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBaseAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.ComponentModel; - -namespace EventSourcing.Backbone -{ - /// - /// Mark for code generation - /// - /// - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public class GenerateEventSourceBaseAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// Type of the generate. - public GenerateEventSourceBaseAttribute(EventSourceGenType generateType) - { - Type = generateType; - } - /// - /// The name of the interface. - /// If missing the generator will use a convention. - /// - public string? Name { get; init; } - - /// - /// The Namespace. - /// If missing the generator will use a convention. - /// - public string? Namespace { get; init; } - - /// - /// Type of the generation - /// - public EventSourceGenType Type { get; } - } -} diff --git a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs deleted file mode 100644 index 4c8aa0cc..00000000 --- a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceBridgeAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace EventSourcing.Backbone -{ - [Obsolete("Deprecated, use GenerateEventSourceAttribute instead", true)] - [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] - public class GenerateEventSourceBridgeAttribute : GenerateEventSourceBaseAttribute - { - public GenerateEventSourceBridgeAttribute(EventSourceGenType generateType) : base(generateType) - { - } - - public string? InterfaceName { get; init; } - } -} From a3635168f3e82aa3a37b25d7805bc1067f72e1c8 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 15:15:13 +0300 Subject: [PATCH 031/178] rfc: EventsContract --- .../Attributes/EventSourceVersion.cs | 4 ++-- ...SourceAttribute.cs => EventsContractAttribute.cs} | 6 +++--- .../{EventSourceGenType.cs => EventsContractType.cs} | 2 +- .../Contracts/IEventFlow.cs | 4 ++-- .../Contracts/IEventFlowStage1.cs | 2 +- .../Contracts/IEventFlowStage2.cs | 2 +- .../Contracts/ISequenceOperations.cs | 12 ++++++------ .../Contracts/Inheritance/IFlowA.cs | 4 ++-- .../Contracts/Inheritance/IFlowAB.cs | 4 ++-- .../Contracts/Inheritance/IFlowB.cs | 4 ++-- .../HelloWorld/IHello.cs | 4 ++-- .../Contracts/ISequenceOperations.cs | 10 +++++----- .../Contracts/ISimpleEvent.cs | 4 ++-- .../Contracts/ISimpleEventTag.cs | 4 ++-- .../Contracts/IEventFlow.cs | 8 ++++---- .../Contracts/IVersionedEvents.cs | 4 ++-- .../ISample.cs | 4 ++-- .../Inheritance/IFlowA.cs | 4 ++-- .../Inheritance/IFlowAB.cs | 4 ++-- .../Inheritance/IFlowB.cs | 4 ++-- .../Concrete/BridgeIncrementalGenerator.cs | 2 +- .../Concrete/ContractIncrementalGenerator.cs | 2 +- 22 files changed, 49 insertions(+), 49 deletions(-) rename EventSourcing.Backbone.Abstractions/Attributes/{GenerateEventSourceAttribute.cs => EventsContractAttribute.cs} (83%) rename EventSourcing.Backbone.Abstractions/Attributes/{EventSourceGenType.cs => EventsContractType.cs} (82%) diff --git a/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs index 8bf11608..5a23d626 100644 --- a/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs @@ -8,12 +8,12 @@ /// We don't expect a gap between ConsumeFrom to a lower version. /// Versions expect to start at 0, if no version specified It will consider version 0, /// - /// + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class EventSourceVersionAttribute : Attribute { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The version which will be produce. public EventSourceVersionAttribute(ushort version) diff --git a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs similarity index 83% rename from EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs rename to EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs index 95f922b5..08a55e8a 100644 --- a/EventSourcing.Backbone.Abstractions/Attributes/GenerateEventSourceAttribute.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs @@ -7,13 +7,13 @@ namespace EventSourcing.Backbone /// /// [AttributeUsage(AttributeTargets.Interface, AllowMultiple = true)] - public class GenerateEventSourceAttribute : Attribute + public class EventsContractAttribute : Attribute { /// /// Initializes a new instance. /// /// Type of the generate. - public GenerateEventSourceAttribute(EventSourceGenType generateType) + public EventsContractAttribute(EventsContractType generateType) { Type = generateType; } @@ -32,6 +32,6 @@ public GenerateEventSourceAttribute(EventSourceGenType generateType) /// /// Type of the generation /// - public EventSourceGenType Type { get; } + public EventsContractType Type { get; } } } diff --git a/EventSourcing.Backbone.Abstractions/Attributes/EventSourceGenType.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventsContractType.cs similarity index 82% rename from EventSourcing.Backbone.Abstractions/Attributes/EventSourceGenType.cs rename to EventSourcing.Backbone.Abstractions/Attributes/EventsContractType.cs index 195970e5..8aba398f 100644 --- a/EventSourcing.Backbone.Abstractions/Attributes/EventSourceGenType.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventsContractType.cs @@ -3,7 +3,7 @@ /// /// Event Source Generation Type /// - public enum EventSourceGenType + public enum EventsContractType { Producer, Consumer diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs index a30737d0..c0bd30c6 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlow.cs @@ -2,8 +2,8 @@ namespace EventSourcing.Backbone.UnitTests.Entities { - [GenerateEventSource(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] public interface IEventFlow { /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs index f917ad4c..ccaddacc 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage1.cs @@ -1,6 +1,6 @@ namespace EventSourcing.Backbone.UnitTests.Entities { - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Consumer)] public interface IEventFlowStage1 { /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs index 8f247a2b..0db12f39 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/IEventFlowStage2.cs @@ -5,7 +5,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities /// /// use to test consumer which have partial operation from IEventFlow /// - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Consumer)] public interface IEventFlowStage2 { /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs index cfae9399..913d9faf 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISequenceOperations.cs @@ -3,12 +3,12 @@ /// /// The sequence operations. /// - //[GenerateEventSource(EventSourceGenType.Producer, Name = "IProducerSequenceOperations", ContractOnly = true)] - [GenerateEventSource(EventSourceGenType.Producer, Name = "IProducerSequenceOperations")] - [GenerateEventSource(EventSourceGenType.Producer)] - //[GenerateEventSourceBridge(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] - //[GenerateEventSourceBridge(EventSourceGenType.Consumer)] + //[EventsContract(EventSourceGenType.Producer, Name = "IProducerSequenceOperations", ContractOnly = true)] + [EventsContract(EventsContractType.Producer, Name = "IProducerSequenceOperations")] + [EventsContract(EventsContractType.Producer)] + //[EventsContractBridge(EventSourceGenType.Producer)] + [EventsContract(EventsContractType.Consumer)] + //[EventsContractBridge(EventSourceGenType.Consumer)] public interface ISequenceOperations { /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs index 061abd0e..82b42d10 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowA.cs @@ -1,7 +1,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities; -[GenerateEventSource(EventSourceGenType.Producer)] -[GenerateEventSource(EventSourceGenType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] public interface IFlowA { ValueTask AAsync(int id); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs index dde32720..5f9999ee 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowAB.cs @@ -1,7 +1,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities; -[GenerateEventSource(EventSourceGenType.Producer)] -[GenerateEventSource(EventSourceGenType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] public interface IFlowAB : IFlowA, IFlowB { ValueTask DerivedAsync(string key); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs index 867874bd..a8a36e10 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/Inheritance/IFlowB.cs @@ -1,7 +1,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities; -[GenerateEventSource(EventSourceGenType.Producer)] -[GenerateEventSource(EventSourceGenType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] public interface IFlowB { ValueTask BAsync(DateTimeOffset date); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs index 032fe76a..7ddebe28 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/IHello.cs @@ -3,8 +3,8 @@ /// /// The sequence operations. /// - [GenerateEventSource(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] public interface IHello { ValueTask HelloAsync(string message); diff --git a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs index e216039e..d20fdb39 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISequenceOperations.cs @@ -1,10 +1,10 @@ namespace EventSourcing.Backbone.UnitTests.Entities { - [GenerateEventSource(EventSourceGenType.Producer, Name = "ISequenceOfProducer")] - [GenerateEventSource(EventSourceGenType.Consumer, Name = "ISequenceOfConsumer")] - [GenerateEventSource(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] - //[GenerateEventSourceBridge(EventSourceGenType.Producer, Name = "ISequenceOfProducer")] + [EventsContract(EventsContractType.Producer, Name = "ISequenceOfProducer")] + [EventsContract(EventsContractType.Consumer, Name = "ISequenceOfConsumer")] + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] + //[EventsContractBridge(EventSourceGenType.Producer, Name = "ISequenceOfProducer")] [Obsolete("Use ISequenceOfProducer or ISequenceOfConsumer", true)] public interface ISequenceOperations { diff --git a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs index 6d4d2d59..cf9641b0 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs @@ -6,8 +6,8 @@ namespace EventSourcing.Backbone.UnitTests.Entities /// /// Test contract /// - [GenerateEventSource(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] [Obsolete("This interface is base for code generation, please use ISimpleEventProducer or ISimpleEventConsumer", true)] [EditorBrowsable(EditorBrowsableState.Never)] public interface ISimpleEvent diff --git a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs index 582c1901..0738c01e 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEventTag.cs @@ -5,8 +5,8 @@ namespace EventSourcing.Backbone.UnitTests.Entities /// /// Test contract /// - [GenerateEventSource(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] [Obsolete("This interface is base for code generation, please use ISimpleEventProducer or ISimpleEventConsumer", true)] [EditorBrowsable(EditorBrowsableState.Never)] public interface ISimpleEventTag : ISimpleEvent diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs index 28f228af..a503183b 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IEventFlow.cs @@ -3,10 +3,10 @@ namespace EventSourcing.Backbone.WebEventTest { - [GenerateEventSource(EventSourceGenType.Consumer)] - [GenerateEventSource(EventSourceGenType.Producer)] - //[GenerateEventSource(EventSourceGenType.Consumer, Namespace = "EventSourcing.Backbone.WebEventTest")] - //[GenerateEventSource(EventSourceGenType.Producer, Namespace = "EventSourcing.Backbone.WebEventTest")] + [EventsContract(EventsContractType.Consumer)] + [EventsContract(EventsContractType.Producer)] + //[EventsContract(EventSourceGenType.Consumer, Namespace = "EventSourcing.Backbone.WebEventTest")] + //[EventsContract(EventSourceGenType.Producer, Namespace = "EventSourcing.Backbone.WebEventTest")] [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Used for code generation, use the producer / consumer version of it", true)] public interface IEventFlow diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs index f80b9677..65c62a51 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IVersionedEvents.cs @@ -2,8 +2,8 @@ namespace EventSourcing.Backbone.WebEventTest { - [GenerateEventSource(EventSourceGenType.Consumer)] - [GenerateEventSource(EventSourceGenType.Producer)] + [EventsContract(EventsContractType.Consumer)] + [EventsContract(EventsContractType.Producer)] [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Used for code generation, use the producer / consumer version of it", true)] public interface IVersionedEvents diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs index 5c6fb918..36122209 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs @@ -3,8 +3,8 @@ /// /// Some doc /// - [GenerateEventSource(EventSourceGenType.Producer)] - [GenerateEventSource(EventSourceGenType.Consumer)] + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] public interface ISample { /// diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs index 061abd0e..82b42d10 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowA.cs @@ -1,7 +1,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities; -[GenerateEventSource(EventSourceGenType.Producer)] -[GenerateEventSource(EventSourceGenType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] public interface IFlowA { ValueTask AAsync(int id); diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs index a41ed8c0..3016e266 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowAB.cs @@ -1,7 +1,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities; -[GenerateEventSource(EventSourceGenType.Producer)] -[GenerateEventSource(EventSourceGenType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] public interface IFlowAB : IFlowA, IFlowB { } diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs index 867874bd..a8a36e10 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/Inheritance/IFlowB.cs @@ -1,7 +1,7 @@ namespace EventSourcing.Backbone.UnitTests.Entities; -[GenerateEventSource(EventSourceGenType.Producer)] -[GenerateEventSource(EventSourceGenType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] public interface IFlowB { ValueTask BAsync(DateTimeOffset date); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 57490e84..9e56c9ca 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -12,7 +12,7 @@ namespace EventSourcing.Backbone [Generator] internal class BridgeIncrementalGenerator : GeneratorIncrementalBase { - private const string TARGET_ATTRIBUTE = "GenerateEventSourceBridge"; + private const string TARGET_ATTRIBUTE = "EventsContractBridge"; public BridgeIncrementalGenerator() : base(TARGET_ATTRIBUTE) { diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 72e37bc8..8bb83a8a 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -13,7 +13,7 @@ namespace EventSourcing.Backbone [Generator] internal class ContractncrementalGenerator : GeneratorIncrementalBase { - private const string TARGET_ATTRIBUTE = "GenerateEventSource"; + private const string TARGET_ATTRIBUTE = "EventsContract"; private readonly BridgeIncrementalGenerator _bridge = new(); public ContractncrementalGenerator() : base(TARGET_ATTRIBUTE) From b5b5bca7bda9be8af9e5e51e8173c38335fc0cb6 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 12:15:49 +0000 Subject: [PATCH 032/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5f4c195f..1763b8c2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.34 + 1.2.35 From 2f0bebe651ad4f6c52c1a499ae12e89599be007c Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 12:16:06 +0000 Subject: [PATCH 033/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1763b8c2..ff29a279 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.35 + 1.2.36 @@ -44,7 +44,7 @@ - + From 4ef020fbdc50c69a83cc584045e8988019db6a8d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 23 May 2023 15:30:22 +0300 Subject: [PATCH 034/178] ci: fix --- .github/workflows/build-publish-v2.yml | 2 +- ...bone.Channels.RedisConsumerProvider.csproj | 17 ----- ...bone.Channels.RedisProducerProvider.csproj | 21 ------ ...kbone.Channels.RedisProvider.Common.csproj | 15 ----- ...kup.Backbone.Consumers.Abstractions.csproj | 16 ----- ...ourcing - Backup.Backbone.Consumers.csproj | 23 ------- ...cing - Backup.Backbone.Abstractions.csproj | 19 ------ .../EventSourcing - Backup.Backbone.csproj | 19 ------ ...kup.Backbone.Producers.Abstractions.csproj | 12 ---- ...ourcing - Backup.Backbone.Producers.csproj | 12 ---- ... - Backup.Backbone.IntegrationTests.csproj | 64 ------------------- ...ourcing - Backup.Backbone.UnitTests.csproj | 46 ------------- ...cing - Backup.Backbone.WebEventTest.csproj | 44 ------------- ...ntSourcing - Backup.Backbone.SrcGen.csproj | 38 ----------- 14 files changed, 1 insertion(+), 347 deletions(-) delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj delete mode 100644 Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj delete mode 100644 Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj delete mode 100644 EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj delete mode 100644 EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj delete mode 100644 Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj delete mode 100644 Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj delete mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj delete mode 100644 Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj delete mode 100644 Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj delete mode 100644 src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj diff --git a/.github/workflows/build-publish-v2.yml b/.github/workflows/build-publish-v2.yml index 07c27009..90f250b3 100644 --- a/.github/workflows/build-publish-v2.yml +++ b/.github/workflows/build-publish-v2.yml @@ -50,7 +50,7 @@ jobs: - name: Build run: dotnet build --configuration ${{ env.BUILD_CONFIG }} --no-restore - name: Test - run: dotnet test Tests/EventSource.Backbone.UnitTests --configuration ${{ env.BUILD_CONFIG }} --no-restore --no-build --verbosity normal + run: dotnet test Tests/EventSourcing.Backbone.UnitTests --configuration ${{ env.BUILD_CONFIG }} --no-restore --no-build --verbosity normal - name: Push generated package to GitHub registry run: dotnet nuget push ./**/*.nupkg -k ${{ secrets.NUGET_PUBLISH }} -s https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj deleted file mode 100644 index 739e3a70..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing - Backup.Backbone.Channels.RedisConsumerProvider.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj deleted file mode 100644 index 97d59326..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing - Backup.Backbone.Channels.RedisProducerProvider.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj deleted file mode 100644 index b710ddb9..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing - Backup.Backbone.Channels.RedisProvider.Common.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj deleted file mode 100644 index 6ddce0ff..00000000 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing - Backup.Backbone.Consumers.Abstractions.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - - EventSourcing.Backbone - - - - - - - - - - - diff --git a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj deleted file mode 100644 index a7d0aeca..00000000 --- a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing - Backup.Backbone.Consumers.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj deleted file mode 100644 index 776e55d3..00000000 --- a/EventSourcing.Backbone.Abstractions/EventSourcing - Backup.Backbone.Abstractions.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - EventSourcing.Backbone - - - - - - - - - - - - - - - diff --git a/EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj b/EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj deleted file mode 100644 index b68940a8..00000000 --- a/EventSourcing.Backbone/EventSourcing - Backup.Backbone.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj deleted file mode 100644 index 35da22b8..00000000 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing - Backup.Backbone.Producers.Abstractions.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj b/Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj deleted file mode 100644 index c23e0607..00000000 --- a/Producers/EventSourcing.Backbone.Producers/EventSourcing - Backup.Backbone.Producers.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj deleted file mode 100644 index 88566adc..00000000 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing - Backup.Backbone.IntegrationTests.csproj +++ /dev/null @@ -1,64 +0,0 @@ - - - - Debug;Release;Gen - false - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj deleted file mode 100644 index 9749fc79..00000000 --- a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing - Backup.Backbone.UnitTests.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - Debug;Release;Gen - false - disable - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj deleted file mode 100644 index f5cb31a8..00000000 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing - Backup.Backbone.WebEventTest.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - Linux - ..\.. - Debug;Release;Gen - True - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj deleted file mode 100644 index 22f2d3b8..00000000 --- a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing - Backup.Backbone.SrcGen.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - netstandard2.0 - true - 10 - enable - true - false - Code generator for EventSourcing.Backbone - Debug;Release;Gen - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - true - False - False - False - - - - - - - - - - - - From 68ee2e2bafd50657beaa4b3101da7177d01e8dbb Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 23 May 2023 12:31:56 +0000 Subject: [PATCH 035/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index ff29a279..8f6b30ee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.36 + 1.2.37 From c9583815af9296b6c1c62e3f4790e23609243fe7 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 12:32:16 +0000 Subject: [PATCH 036/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8f6b30ee..a5edc002 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.37 + 1.2.38 @@ -44,7 +44,7 @@ - + From 9f6ceefb73fd777ef74785c9a2186d27c4bab583 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 12:37:01 +0000 Subject: [PATCH 037/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a5edc002..f3ad493f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.38 + 1.2.39 @@ -44,7 +44,7 @@ - + From f6e38f9136e01163eabdf64609aa2034192dfa44 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 23 May 2023 12:43:38 +0000 Subject: [PATCH 038/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f3ad493f..e69539ee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.39 + 1.2.40 @@ -44,7 +44,7 @@ - + From 1f220250d5db6144f322233c55b3164ff3bddc7f Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 10:54:52 +0300 Subject: [PATCH 039/178] rfc: remove level of abstraction --- ...bone.Channels.RedisConsumerProvider.csproj | 1 + .../RedisConsumerBuilder.cs | 2 +- .../RedisConsumerChannel.cs | 4 +- .../RedisProducerChannel.cs | 255 +++++++++--------- .../Factory/RedisClientFactory.cs | 21 +- .../Factory/RedisConnectionFacrotyBase.cs | 6 +- .../Factory/RedisCredentialsRaw.cs | 9 +- .../RedisTelemetryrExtensions.cs | 45 ++-- ...ne.Channels.S3StoreConsumerProvider.csproj | 1 - ...ne.Channels.S3StoreProducerProvider.csproj | 1 - .../BlobResponse.cs | 2 +- ...ing.Backbone.Consumers.Abstractions.csproj | 28 -- .../TelemetryrExtensions.cs | 37 --- .../ConsumerBase.EventSourceSubscriber.cs | 2 +- .../Builder/ConsumerBuilder.cs | 2 +- .../EventSourcing.Backbone.Consumers.csproj | 1 - .../Attributes/EventsContractAttribute.cs | 4 +- .../Consumer}/Ack/Ack.cs | 0 .../Consumer}/Ack/AckBehavior.cs | 0 .../Consumer}/Ack/AckOnce.cs | 0 .../Consumer}/Ack/IAck.cs | 0 .../Consumer}/Attributes.cs | 0 .../Building/IConsumerFilterBuilder.cs | 0 .../Builder/Building/IConsumerHooksBuilder.cs | 0 .../Building/IConsumerOptionsBuilder.cs | 0 .../Builder/Building/IConsumerReadyBuilder.cs | 0 .../IConsumerStorageStrategyWithFilter.cs | 0 .../Building/IConsumerStoreStrategyBuilder.cs | 0 .../Building/IConsumerSubscribeBuilder.cs | 0 .../IConsumerSubscribtionHubBuilder.cs | 0 .../ConsumerAsyncEnumerableJsonOptions.cs | 0 .../ConsumerAsyncEnumerableOptions.cs | 0 .../Building/Receiver/IConsumerIterator.cs | 0 .../Receiver/IConsumerIteratorCommands.cs | 0 .../Building/Receiver/IConsumerReceiver.cs | 0 .../Receiver/IConsumerReceiverCommands.cs | 0 .../Route/IConsumerEnvironmentBuilder.cs | 0 .../Route/IConsumerEnvironmentOfBuilder.cs | 0 .../Building/Route/IConsumerUriBuilder.cs | 0 .../Consumer}/Builder/IConsumer.cs | 0 .../Consumer}/Builder/IConsumerBuilder.cs | 0 .../Consumer}/Builder/IConsumerLifetime.cs | 0 .../Consumer}/Builder/IConsumerPlan.cs | 0 .../Consumer}/Builder/IConsumerPlanBase.cs | 0 .../Consumer}/Builder/IConsumerPlanBuilder.cs | 0 .../Consumer}/ClaimingTrigger.cs | 0 .../Consumer}/ConsumerMetadata.cs | 0 .../Consumer}/ConsumerOptions.cs | 0 .../Consumer}/Enums/MultiConsumerBehavior.cs | 0 .../Enums/PartialConsumerBehavior.cs | 0 .../EventSourceConsumer.deprecated.cs | 0 .../Consumer}/IConsumerEntityMapper.cs | 0 .../Interceptors/ConsumerInterceptorBridge.cs | 0 .../Interceptors/IConsumerAsyncInterceptor.cs | 0 .../Interceptors/IConsumerInterceptor.cs | 0 .../Provider/IConsumerChannelProvider.cs | 0 .../Provider/IConsumerStorageStrategy.cs | 0 .../ConsumerSegmentationStrategyBridge.cs | 0 .../IConsumerAsyncSegmenationStrategy.cs | 0 .../IConsumerSegmenationStrategy.cs | 0 .../EventSourcingException.cs | 7 +- .../Extensions/EventSourcingExtensions.cs | 63 +++++ .../Extensions/TelemetryrExtensions.cs | 47 ++++ .../Building/IProducerBuilderEnvironment.cs | 0 .../Building/IProducerEnvironmentBuilder.cs | 0 .../Builder/Building/IProducerHooksBuilder.cs | 0 .../Building/IProducerLoggerBuilder.cs | 0 .../Building/IProducerOptionsBuilder.cs | 0 .../Builder/Building/IProducerRawBuilder.cs | 0 .../Building/IProducerSpecializeBuilder.cs | 0 .../IProducerStorageStrategyWithFilter.cs | 0 .../Building/IProducerStoreStrategyBuilder.cs | 0 .../Builder/Building/IProducerUriBuilder.cs | 0 .../Builder/Building/IRawProducer.cs | 4 +- .../Override/IProducerOverrideBuildBuilder.cs | 0 .../Override/IProducerOverrideBuilder.cs | 0 .../IProducerOverrideEnvironmentBuilder.cs | 0 .../Override/IProducerOverrideUriBuilder.cs | 0 .../Builder/Building/RawProducerOptions.cs | 0 .../Builder/Building/RouteAssignmentType.cs | 0 .../Producer}/Builder/IProducerBuilder.cs | 0 .../Interceptors/IProducerAsyncInterceptor.cs | 0 .../Interceptors/IProducerInterceptor.cs | 0 .../Interceptors/ProducerInterceptorBridge.cs | 0 .../Producer}/Plan/IProducerPlan.cs | 0 .../Producer}/Plan/IProducerPlanBase.cs | 0 .../Producer}/Plan/IProducerPlanBuilder.cs | 0 .../Producer}/Plan/ProducerPlan.cs | 0 .../Producer}/ProducerExtensions.cs | 0 .../Producer}/ProducerPipeline.cs | 0 .../Provider/IProducerChannelProvider.cs | 0 .../Provider/IProducerStorageStrategy.cs | 0 .../IProducerAsyncSegmentationStrategy.cs | 0 .../IProducerSegmentationStrategy.cs | 0 .../ProducerSegmentationStrategyBridge.cs | 0 .../TelemetryrExtensions.cs | 36 --- EventSourcing.Backbone.sln | 25 +- PlayGroundConsumer/IFooX.cs | 7 + PlayGroundConsumer/PlayGroundConsumer.csproj | 26 ++ PlayGroundConsumer/Program.cs | 2 + .../icon.png | Bin ...ing.Backbone.Producers.Abstractions.csproj | 23 -- .../TelemetryrExtensions.cs | 47 ---- .../EventSourcing.Backbone.Producers.csproj | 1 - .../EndToEndStressTests.cs | 4 +- ...tSourcing.Backbone.IntegrationTests.csproj | 2 - .../HelloWorld/HelloWorldTests.cs | 15 -- .../InheritanceTests.cs | 2 +- .../MigrationTest.cs | 2 - .../TestsBase.cs | 9 - .../EventSourcing.Backbone.UnitTests.csproj | 2 - .../AspCoreExtensions.cs | 6 +- ...Sourcing.Backbone.SrcGen.Playground.csproj | 7 +- .../Concrete/BridgeIncrementalGenerator.cs | 7 +- .../Concrete/ContractIncrementalGenerator.cs | 2 + .../Properties/launchSettings.json | 2 +- src-gen/PlayGroundAbstraction/IFoo.cs | 10 + .../PlayGroundAbstraction.csproj | 25 ++ .../PlayGroundAbstraction}/icon.png | Bin src-gen/PlayGroundProducer/IFooY.cs | 7 + .../PlayGroundProducer.csproj | 28 ++ src-gen/PlayGroundProducer/Program.cs | 2 + src-gen/PlayGroundProducer/icon.png | Bin 0 -> 58881 bytes 123 files changed, 414 insertions(+), 427 deletions(-) delete mode 100644 Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj delete mode 100644 Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Ack/Ack.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Ack/AckBehavior.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Ack/AckOnce.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Ack/IAck.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Attributes.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerFilterBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerHooksBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerOptionsBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerReadyBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerStorageStrategyWithFilter.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerStoreStrategyBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerSubscribeBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/IConsumerSubscribtionHubBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Receiver/IConsumerIterator.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Receiver/IConsumerIteratorCommands.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Receiver/IConsumerReceiver.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Receiver/IConsumerReceiverCommands.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Route/IConsumerEnvironmentBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/Building/Route/IConsumerUriBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/IConsumer.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/IConsumerBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/IConsumerLifetime.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/IConsumerPlan.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/IConsumerPlanBase.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Builder/IConsumerPlanBuilder.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/ClaimingTrigger.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/ConsumerMetadata.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/ConsumerOptions.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Enums/MultiConsumerBehavior.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Enums/PartialConsumerBehavior.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/EventSourceConsumer.deprecated.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/IConsumerEntityMapper.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Interceptors/ConsumerInterceptorBridge.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Interceptors/IConsumerAsyncInterceptor.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Interceptors/IConsumerInterceptor.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Provider/IConsumerChannelProvider.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Provider/IConsumerStorageStrategy.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Segmentation/ConsumerSegmentationStrategyBridge.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Segmentation/IConsumerAsyncSegmenationStrategy.cs (100%) rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => EventSourcing.Backbone.Abstractions/Consumer}/Segmentation/IConsumerSegmenationStrategy.cs (100%) create mode 100644 EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs create mode 100644 EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerBuilderEnvironment.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerEnvironmentBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerHooksBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerLoggerBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerOptionsBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerRawBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerSpecializeBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerStorageStrategyWithFilter.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerStoreStrategyBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IProducerUriBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/IRawProducer.cs (95%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/Override/IProducerOverrideBuildBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/Override/IProducerOverrideBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/Override/IProducerOverrideUriBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/RawProducerOptions.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/Building/RouteAssignmentType.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Builder/IProducerBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Interceptors/IProducerAsyncInterceptor.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Interceptors/IProducerInterceptor.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Interceptors/ProducerInterceptorBridge.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Plan/IProducerPlan.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Plan/IProducerPlanBase.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Plan/IProducerPlanBuilder.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Plan/ProducerPlan.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/ProducerExtensions.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/ProducerPipeline.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Provider/IProducerChannelProvider.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Provider/IProducerStorageStrategy.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Segmentation/IProducerAsyncSegmentationStrategy.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Segmentation/IProducerSegmentationStrategy.cs (100%) rename {Producers/EventSourcing.Backbone.Producers.Abstractions => EventSourcing.Backbone.Abstractions/Producer}/Segmentation/ProducerSegmentationStrategyBridge.cs (100%) delete mode 100644 EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs create mode 100644 PlayGroundConsumer/IFooX.cs create mode 100644 PlayGroundConsumer/PlayGroundConsumer.csproj create mode 100644 PlayGroundConsumer/Program.cs rename {Consumers/EventSourcing.Backbone.Consumers.Abstractions => PlayGroundConsumer}/icon.png (100%) delete mode 100644 Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj delete mode 100644 Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs create mode 100644 src-gen/PlayGroundAbstraction/IFoo.cs create mode 100644 src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj rename {Producers/EventSourcing.Backbone.Producers.Abstractions => src-gen/PlayGroundAbstraction}/icon.png (100%) create mode 100644 src-gen/PlayGroundProducer/IFooY.cs create mode 100644 src-gen/PlayGroundProducer/PlayGroundProducer.csproj create mode 100644 src-gen/PlayGroundProducer/Program.cs create mode 100644 src-gen/PlayGroundProducer/icon.png diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj index 7bd16756..63b66eca 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj @@ -15,6 +15,7 @@ + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs index e37a10db..e7fcd7eb 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs @@ -44,7 +44,7 @@ public static IConsumerStoreStrategyBuilder Create( /// The redis configuration. /// The setting. /// - public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder ( + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( this ConfigurationOptions options, RedisConsumerChannelSetting? setting = null) { diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index a33721bf..9f50a106 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -206,7 +206,7 @@ private async Task SubsribeToSingleAsync( var claimingTrigger = options.ClaimingTrigger; var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - string key = plan.FullUri(); + string key = plan.FullUri(); bool isFirstBatchOrFailure = true; CommandFlags flags = CommandFlags.None; @@ -639,7 +639,7 @@ await db.StreamAcknowledgeAsync(key, messageId, flags: CommandFlags.DemandMaster); } - catch(Exception ex) + catch (Exception ex) { // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class logger.LogWarning(ex.FormatLazy(), $"Fail to acknowledge message [{messageId}]"); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 7c4d2ddc..0a0d6b06 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -12,167 +12,166 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -namespace EventSourcing.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider; + +internal class RedisProducerChannel : IProducerChannelProvider { - internal class RedisProducerChannel : IProducerChannelProvider + private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); + private readonly ILogger _logger; + private readonly AsyncPolicy _resiliencePolicy; + private readonly IEventSourceRedisConnectionFacroty _connFactory; + private readonly IProducerStorageStrategy _defaultStorageStrategy; + private const string META_SLOT = "____"; + + #region Ctor + + /// + /// Initializes a new instance. + /// + /// The redis database promise. + /// The logger. + /// The resilience policy for retry. + public RedisProducerChannel( + IEventSourceRedisConnectionFacroty redisFactory, + ILogger logger, + AsyncPolicy? resiliencePolicy) { - private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); - private readonly ILogger _logger; - private readonly AsyncPolicy _resiliencePolicy; - private readonly IEventSourceRedisConnectionFacroty _connFactory; - private readonly IProducerStorageStrategy _defaultStorageStrategy; - private const string META_SLOT = "____"; - - #region Ctor - - /// - /// Initializes a new instance. - /// - /// The redis database promise. - /// The logger. - /// The resilience policy for retry. - public RedisProducerChannel( - IEventSourceRedisConnectionFacroty redisFactory, - ILogger logger, - AsyncPolicy? resiliencePolicy) - { - _connFactory = redisFactory; - _logger = logger; - _resiliencePolicy = resiliencePolicy ?? - Policy.Handle() - .RetryAsync(3); - _defaultStorageStrategy = new RedisHashStorageStrategy(_connFactory, logger); - } + _connFactory = redisFactory; + _logger = logger; + _resiliencePolicy = resiliencePolicy ?? + Policy.Handle() + .RetryAsync(3); + _defaultStorageStrategy = new RedisHashStorageStrategy(_connFactory, logger); + } - #endregion // Ctor + #endregion // Ctor - #region SendAsync + #region SendAsync - /// - /// Sends raw announcement. - /// - /// The raw announcement data. - /// The storage strategy. - /// - /// Return the message id - /// - public async ValueTask SendAsync( - Announcement payload, - ImmutableArray storageStrategy) - { - Metadata meta = payload.Metadata; - string id = meta.MessageId; + /// + /// Sends raw announcement. + /// + /// The raw announcement data. + /// The storage strategy. + /// + /// Return the message id + /// + public async ValueTask SendAsync( + Announcement payload, + ImmutableArray storageStrategy) + { + Metadata meta = payload.Metadata; + string id = meta.MessageId; + + #region var entries = new NameValueEntry[]{...} - #region var entries = new NameValueEntry[]{...} + string metaJson = JsonSerializer.Serialize(meta, EventSourceOptions.FullSerializerOptions); - string metaJson = JsonSerializer.Serialize(meta, EventSourceOptions.FullSerializerOptions); + // local method + NameValueEntry KV(RedisValue key, RedisValue value) => new NameValueEntry(key, value); + ImmutableArray commonEntries = ImmutableArray.Create( + KV(nameof(meta.MessageId), id), + KV(nameof(meta.Operation), meta.Operation), + KV(nameof(meta.ProducedAt), meta.ProducedAt.ToUnixTimeSeconds()), + KV(nameof(meta.ChannelType), CHANNEL_TYPE), + KV(nameof(meta.Origin), meta.Origin.ToString()), + KV(META_SLOT, metaJson) + ); - // local method - NameValueEntry KV(RedisValue key, RedisValue value) => new NameValueEntry(key, value); - ImmutableArray commonEntries = ImmutableArray.Create( - KV(nameof(meta.MessageId), id), - KV(nameof(meta.Operation), meta.Operation), - KV(nameof(meta.ProducedAt), meta.ProducedAt.ToUnixTimeSeconds()), - KV(nameof(meta.ChannelType), CHANNEL_TYPE), - KV(nameof(meta.Origin), meta.Origin.ToString()), - KV(META_SLOT, metaJson) - ); + #endregion // var entries = new NameValueEntry[]{...} - #endregion // var entries = new NameValueEntry[]{...} + RedisValue messageId = await _resiliencePolicy.ExecuteAsync(LocalStreamAddAsync); - RedisValue messageId = await _resiliencePolicy.ExecuteAsync(LocalStreamAddAsync); + return (string?)messageId ?? "0000000000000-0"; - return (string?)messageId ?? "0000000000000-0"; + #region LocalStreamAddAsync + + async Task LocalStreamAddAsync() + { + await LocalStoreBucketAsync(EventBucketCategories.Segments); + await LocalStoreBucketAsync(EventBucketCategories.Interceptions); - #region LocalStreamAddAsync + var telemetryBuilder = commonEntries.ToBuilder(); + var activityName = $"{meta.Operation} produce"; + using Activity? activity = ACTIVITY_SOURCE.StartActivity(activityName, ActivityKind.Producer); + activity.InjectSpan(meta, telemetryBuilder, LocalInjectTelemetry); + meta.InjectTelemetryTags(activity); + var entries = telemetryBuilder.ToArray(); - async Task LocalStreamAddAsync() + try { - await LocalStoreBucketAsync(EventBucketCategories.Segments); - await LocalStoreBucketAsync(EventBucketCategories.Interceptions); + IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IDatabaseAsync db = conn.GetDatabase(); + using var scope = SuppressInstrumentationScope.Begin(); + var k = meta.FullUri(); + var result = await db.StreamAddAsync(k, entries, + flags: CommandFlags.DemandMaster); + return result; + } + #region Exception Handling + + catch (RedisConnectionException ex) + { + _logger.LogError(ex, "REDIS Connection Failure: push event [{id}] into the [{URI}] stream: {operation}", + meta.MessageId, meta.Uri, meta.Operation); + throw; + } + catch (Exception ex) + { + _logger.LogError(ex, "Fail to push event [{id}] into the [{URI}] stream: {operation}", + meta.MessageId, meta.Uri, meta.Operation); + throw; + } - var telemetryBuilder = commonEntries.ToBuilder(); - var activityName = $"{meta.Operation} produce"; - using Activity? activity = ACTIVITY_SOURCE.StartActivity(activityName, ActivityKind.Producer); - activity.InjectSpan(meta, telemetryBuilder, LocalInjectTelemetry); - meta.InjectTelemetryTags(activity); - var entries = telemetryBuilder.ToArray(); + #endregion // Exception Handling - try - { - IConnectionMultiplexer conn = await _connFactory.GetAsync(); - IDatabaseAsync db = conn.GetDatabase(); - using var scope = SuppressInstrumentationScope.Begin(); - var k = meta.FullUri(); - var result = await db.StreamAddAsync(k, entries, - flags: CommandFlags.DemandMaster); - return result; - } - #region Exception Handling + #region ValueTask StoreBucketAsync(StorageType storageType) // local function - catch (RedisConnectionException ex) + async ValueTask LocalStoreBucketAsync(EventBucketCategories storageType) + { + var strategies = storageStrategy.Where(m => m.IsOfTargetType(storageType)); + Bucket bucket = storageType == EventBucketCategories.Segments ? payload.Segments : payload.InterceptorsData; + if (strategies.Any()) { - _logger.LogError(ex, "REDIS Connection Failure: push event [{id}] into the [{URI}] stream: {operation}", - meta.MessageId, meta.Uri, meta.Operation); - throw; + foreach (var strategy in strategies) + { + await SaveBucketAsync(strategy); + } } - catch (Exception ex) + else { - _logger.LogError(ex, "Fail to push event [{id}] into the [{URI}] stream: {operation}", - meta.MessageId, meta.Uri, meta.Operation); - throw; + await SaveBucketAsync(_defaultStorageStrategy); } - #endregion // Exception Handling - - #region ValueTask StoreBucketAsync(StorageType storageType) // local function - - async ValueTask LocalStoreBucketAsync(EventBucketCategories storageType) + async ValueTask SaveBucketAsync(IProducerStorageStrategy strategy) { - var strategies = storageStrategy.Where(m => m.IsOfTargetType(storageType)); - Bucket bucket = storageType == EventBucketCategories.Segments ? payload.Segments : payload.InterceptorsData; - if (strategies.Any()) - { - foreach (var strategy in strategies) - { - await SaveBucketAsync(strategy); - } - } - else + IImmutableDictionary metaItems = + await strategy.SaveBucketAsync(id, bucket, storageType, meta); + foreach (var item in metaItems) { - await SaveBucketAsync(_defaultStorageStrategy); + commonEntries = commonEntries.Add(KV(item.Key, item.Value)); } - async ValueTask SaveBucketAsync(IProducerStorageStrategy strategy) - { - IImmutableDictionary metaItems = - await strategy.SaveBucketAsync(id, bucket, storageType, meta); - foreach (var item in metaItems) - { - commonEntries = commonEntries.Add(KV(item.Key, item.Value)); - } - - } } + } - #endregion // ValueTask StoreBucketAsync(StorageType storageType) // local function - #region LocalInjectTelemetry - - void LocalInjectTelemetry( - ImmutableArray.Builder builder, - string key, - string value) - { - builder.Add(KV(key, value)); - } + #endregion // ValueTask StoreBucketAsync(StorageType storageType) // local function + #region LocalInjectTelemetry - #endregion // LocalInjectTelemetry + void LocalInjectTelemetry( + ImmutableArray.Builder builder, + string key, + string value) + { + builder.Add(KV(key, value)); } - #endregion // LocalStreamAddAsync + #endregion // LocalInjectTelemetry } - #endregion // SendAsync + #endregion // LocalStreamAddAsync } + + #endregion // SendAsync } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs index f74c7fc8..3937b0aa 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisClientFactory.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Reflection; +using System.Reflection; using System.Text; using Microsoft.Extensions.Logging; @@ -63,18 +62,18 @@ public static ConfigurationOptions CreateConfigurationOptions( Action? configurationHook = null) { var (endpoint, password) = credential switch - { - RedisCredentialsRaw raw => (raw.Endpoint, raw.Password), - RedisCredentialsEnvKeys env => ( - Environment.GetEnvironmentVariable(env.Endpoint ?? END_POINT_KEY), - Environment.GetEnvironmentVariable(env.Password ?? PASSWORD_KEY) - ), - _ => throw new InvalidOperationException(credential?.GetType()?.Name) - }; + { + RedisCredentialsRaw raw => (raw.Endpoint, raw.Password), + RedisCredentialsEnvKeys env => ( + Environment.GetEnvironmentVariable(env.Endpoint ?? END_POINT_KEY), + Environment.GetEnvironmentVariable(env.Password ?? PASSWORD_KEY) + ), + _ => throw new InvalidOperationException(credential?.GetType()?.Name) + }; #region Validation - if(string.IsNullOrEmpty(endpoint)) + if (string.IsNullOrEmpty(endpoint)) throw new InvalidOperationException($"{nameof(endpoint)} is null"); #endregion // Validation diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs index 561e31e4..9fcda858 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs @@ -1,6 +1,4 @@ -using System.Net; - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -80,7 +78,7 @@ protected RedisConnectionFacrotyBase( IRedisCredentials credential, ILogger logger, Action? configurationHook = null) - + { _logger = logger; _configuration = credential.CreateConfigurationOptions(configurationHook); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs index b19c3659..85b1db87 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsRaw.cs @@ -1,12 +1,9 @@ -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; - - -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone { /// /// Raw keys for REDIS's credentials /// - public record RedisCredentialsRaw: IRedisCredentials + public record RedisCredentialsRaw : IRedisCredentials { #region Ctor @@ -27,7 +24,7 @@ public RedisCredentialsRaw(string endpoint, string? password = null) /// /// The raw endpoint (not an environment variable) /// - public string? Endpoint { get; } + public string? Endpoint { get; } /// /// The password (not an environment variable). /// diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs index f57a94c0..200332e8 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs @@ -1,31 +1,30 @@ using System.Diagnostics; -namespace EventSourcing.Backbone -{ - public static class RedisTelemetryrExtensions - { - #region InjectTelemetryTags +namespace EventSourcing.Backbone; - /// - /// Adds standard open-telemetry tags (for redis). - /// - /// The meta. - /// The activity. - public static void InjectTelemetryTags(this Metadata meta, Activity? activity) - { - // These tags are added demonstrating the semantic conventions of the OpenTelemetry messaging specification - // See: - // * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#messaging-attributes - activity?.SetTag("messaging.system", "redis-stream"); - activity?.SetTag("messaging.destination_kind", "topic"); +public static class RedisTelemetryrExtensions +{ + #region InjectTelemetryTags - activity?.SetTag("messaging.destination", meta.Operation); - activity?.SetTag("messaging.message_id", meta.MessageId); - activity?.SetTag("messaging.redis.key", meta.Uri); + /// + /// Adds standard open-telemetry tags (for redis). + /// + /// The meta. + /// The activity. + public static void InjectTelemetryTags(this Metadata meta, Activity? activity) + { + // These tags are added demonstrating the semantic conventions of the OpenTelemetry messaging specification + // See: + // * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#messaging-attributes + activity?.SetTag("messaging.system", "redis-stream"); + activity?.SetTag("messaging.destination_kind", "topic"); - meta.InjectMetaTelemetryTags(activity); - } + activity?.SetTag("messaging.destination", meta.Operation); + activity?.SetTag("messaging.message_id", meta.MessageId); + activity?.SetTag("messaging.redis.key", meta.Uri); - #endregion // InjectTelemetryTags + meta.InjectMetaTelemetryTags(activity); } + + #endregion // InjectTelemetryTags } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj index b57fbdb6..b2435eb0 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj @@ -13,7 +13,6 @@ - diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj index de25c04d..d71efa88 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj @@ -13,7 +13,6 @@ - diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs index bfc999a3..c5c623f0 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs @@ -134,7 +134,7 @@ public bool Equals(BlobResponse? other) public override int GetHashCode() { return HashCode.Combine( - _key, + _key, _fileName, _eTag, _contentVersion); diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj deleted file mode 100644 index 5b0929b7..00000000 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourcing.Backbone.Consumers.Abstractions.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - - EventSourcing.Backbone - - - - README.md - - - - - True - \ - - - - - - - - - - - - - diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs b/Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs deleted file mode 100644 index 00a60ede..00000000 --- a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/TelemetryrExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Diagnostics; - -using OpenTelemetry; -using OpenTelemetry.Context.Propagation; - -namespace EventSourcing.Backbone -{ - public static class TelemetryrExtensions - { - private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; - - #region ExtractSpan - - /// - /// Extract telemetry span's parent info - /// - /// - /// The meta. - /// The entries for extraction. - /// The injection strategy. - /// - public static ActivityContext ExtractSpan( - this Metadata meta, - T entries, - Func> injectStrategy) - { - PropagationContext parentContext = Propagator.Extract(default, entries, injectStrategy); - Baggage.Current = parentContext.Baggage; - - // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - return parentContext.ActivityContext; - } - - #endregion // ExtractSpan - } -} diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index cb7adb8c..c7584daa 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -126,7 +126,7 @@ private async ValueTask ConsumingAsync( if (_maxMessages != 0 && _maxMessages < count) { await DisposeAsync(); - throw new OperationCanceledException(); + throw new OperationCanceledException(); } #endregion // Increment & Validation Max Messages Limit diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index fa43dc16..f45f48e4 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -568,7 +568,7 @@ private static JsonElement ToJson( { encoded = Encoding.UTF8.GetString(val.Span); } - catch + catch { plan.Logger.LogDebug("Failed to encode value of ({key})", key); } diff --git a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj index 704aeb1b..ae19406e 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj +++ b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj @@ -12,7 +12,6 @@ - diff --git a/EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs index 08a55e8a..3c4de4c0 100644 --- a/EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventsContractAttribute.cs @@ -1,6 +1,4 @@ -using System.ComponentModel; - -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone { /// /// Mark for code generation diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/Ack.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/Ack.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/Ack.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Ack/Ack.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckBehavior.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckBehavior.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckBehavior.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Ack/AckBehavior.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckOnce.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/AckOnce.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/IAck.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAck.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Ack/IAck.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Ack/IAck.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Attributes.cs b/EventSourcing.Backbone.Abstractions/Consumer/Attributes.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Attributes.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Attributes.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerFilterBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerFilterBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerFilterBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerFilterBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerHooksBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerHooksBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerHooksBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerOptionsBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerOptionsBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerOptionsBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerOptionsBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerReadyBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerReadyBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerReadyBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStorageStrategyWithFilter.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStorageStrategyWithFilter.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStorageStrategyWithFilter.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStorageStrategyWithFilter.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStoreStrategyBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStoreStrategyBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerStoreStrategyBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStoreStrategyBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribeBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribeBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribeBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribtionHubBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribtionHubBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/IConsumerSubscribtionHubBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribtionHubBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/ConsumerAsyncEnumerableJsonOptions.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/ConsumerAsyncEnumerableOptions.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerIterator.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIterator.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerIterator.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIteratorCommands.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerIteratorCommands.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerIteratorCommands.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerIteratorCommands.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerReceiver.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiver.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerReceiver.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiverCommands.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerReceiverCommands.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Receiver/IConsumerReceiverCommands.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Receiver/IConsumerReceiverCommands.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerUriBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/Building/Route/IConsumerUriBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerUriBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumer.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumer.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumer.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumer.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerLifetime.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerLifetime.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerLifetime.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerLifetime.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerPlan.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlan.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerPlan.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBase.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerPlanBase.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBase.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerPlanBase.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerPlanBuilder.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Builder/IConsumerPlanBuilder.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerPlanBuilder.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ClaimingTrigger.cs b/EventSourcing.Backbone.Abstractions/Consumer/ClaimingTrigger.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/ClaimingTrigger.cs rename to EventSourcing.Backbone.Abstractions/Consumer/ClaimingTrigger.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerMetadata.cs rename to EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerOptions.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/ConsumerOptions.cs rename to EventSourcing.Backbone.Abstractions/Consumer/ConsumerOptions.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/MultiConsumerBehavior.cs b/EventSourcing.Backbone.Abstractions/Consumer/Enums/MultiConsumerBehavior.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/MultiConsumerBehavior.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Enums/MultiConsumerBehavior.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/PartialConsumerBehavior.cs b/EventSourcing.Backbone.Abstractions/Consumer/Enums/PartialConsumerBehavior.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Enums/PartialConsumerBehavior.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Enums/PartialConsumerBehavior.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourceConsumer.deprecated.cs b/EventSourcing.Backbone.Abstractions/Consumer/EventSourceConsumer.deprecated.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/EventSourceConsumer.deprecated.cs rename to EventSourcing.Backbone.Abstractions/Consumer/EventSourceConsumer.deprecated.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerEntityMapper.cs b/EventSourcing.Backbone.Abstractions/Consumer/IConsumerEntityMapper.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/IConsumerEntityMapper.cs rename to EventSourcing.Backbone.Abstractions/Consumer/IConsumerEntityMapper.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/ConsumerInterceptorBridge.cs b/EventSourcing.Backbone.Abstractions/Consumer/Interceptors/ConsumerInterceptorBridge.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/ConsumerInterceptorBridge.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Interceptors/ConsumerInterceptorBridge.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerAsyncInterceptor.cs b/EventSourcing.Backbone.Abstractions/Consumer/Interceptors/IConsumerAsyncInterceptor.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerAsyncInterceptor.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Interceptors/IConsumerAsyncInterceptor.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerInterceptor.cs b/EventSourcing.Backbone.Abstractions/Consumer/Interceptors/IConsumerInterceptor.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Interceptors/IConsumerInterceptor.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Interceptors/IConsumerInterceptor.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerChannelProvider.cs b/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerChannelProvider.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerChannelProvider.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerChannelProvider.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerStorageStrategy.cs b/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerStorageStrategy.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Provider/IConsumerStorageStrategy.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerStorageStrategy.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/ConsumerSegmentationStrategyBridge.cs b/EventSourcing.Backbone.Abstractions/Consumer/Segmentation/ConsumerSegmentationStrategyBridge.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/ConsumerSegmentationStrategyBridge.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Segmentation/ConsumerSegmentationStrategyBridge.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerAsyncSegmenationStrategy.cs b/EventSourcing.Backbone.Abstractions/Consumer/Segmentation/IConsumerAsyncSegmenationStrategy.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerAsyncSegmenationStrategy.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Segmentation/IConsumerAsyncSegmenationStrategy.cs diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerSegmenationStrategy.cs b/EventSourcing.Backbone.Abstractions/Consumer/Segmentation/IConsumerSegmenationStrategy.cs similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/Segmentation/IConsumerSegmenationStrategy.cs rename to EventSourcing.Backbone.Abstractions/Consumer/Segmentation/IConsumerSegmenationStrategy.cs diff --git a/EventSourcing.Backbone.Abstractions/EventSourcingException.cs b/EventSourcing.Backbone.Abstractions/EventSourcingException.cs index 68b91513..c063d36d 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcingException.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourcingException.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.Serialization; namespace EventSourcing.Backbone { diff --git a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs new file mode 100644 index 00000000..ad8bb49e --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs @@ -0,0 +1,63 @@ +using System.Diagnostics; + +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Trace; + +using static System.Diagnostics.TelemetryrExtensions; +namespace EventSourcing.Backbone; + +public static class EventSourcingExtensions +{ + /// + /// Adds the event consumer telemetry source (will result in tracing the consumer). + /// + /// The builder. + /// + public static TracerProviderBuilder ListenToEventSourceRedisChannel(this TracerProviderBuilder builder) => + builder.AddSource( + EventSourceConstants.REDIS_CONSUMER_CHANNEL_SOURCE, + EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); + + #region ExtractSpan + + /// + /// Extract telemetry span's parent info + /// + /// + /// The meta. + /// The entries for extraction. + /// The injection strategy. + /// + public static ActivityContext ExtractSpan( + this Metadata meta, + T entries, + Func> injectStrategy) + { + PropagationContext parentContext = Propagator.Extract(default, entries, injectStrategy); + Baggage.Current = parentContext.Baggage; + + // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + return parentContext.ActivityContext; + } + + #endregion // ExtractSpan + + #region InjectMetaTelemetryTags + + /// + /// Adds standard open-telemetry tags (for redis). + /// + /// The meta. + /// The activity. + public static void InjectMetaTelemetryTags(this Metadata meta, Activity? activity) + { + activity?.SetTag("event-source.uri", meta.Uri); + activity?.SetTag("event-source.operation", meta.Operation); + activity?.SetTag("event-source.message-id", meta.MessageId); + activity?.SetTag("event-source.channel-type", meta.ChannelType); + } + + #endregion // InjectMetaTelemetryTags +} diff --git a/EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs b/EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs new file mode 100644 index 00000000..680ffe15 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs @@ -0,0 +1,47 @@ +using System.Collections.Immutable; + +using EventSourcing.Backbone; + +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; + +namespace System.Diagnostics; + +public static class TelemetryrExtensions +{ + internal static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; + + #region StartSpanScope + + /// + /// Inject telemetry span to the channel property. + /// + /// + /// The activity. + /// The meta. + /// The entries builder. + /// The injection strategy. + /// + public static void InjectSpan( + this Activity? activity, + Metadata meta, + ImmutableArray.Builder entriesBuilder, + Action.Builder, string, string> injectStrategy) + { + // Depending on Sampling (and whether a listener is registered or not), the + // activity above may not be created. + // If it is created, then propagate its context. + // If it is not created, the propagate the Current context, + // if any. + if (activity != null) + { + Activity.Current = activity; + } + ActivityContext contextToInject = Activity.Current?.Context ?? default; + + // Inject the ActivityContext into the message metadata to propagate trace context to the receiving service. + Propagator.Inject(new PropagationContext(contextToInject, Baggage.Current), entriesBuilder, injectStrategy); + } + + #endregion // StartSpanScope +} diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerBuilderEnvironment.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerBuilderEnvironment.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerBuilderEnvironment.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerBuilderEnvironment.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerEnvironmentBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerEnvironmentBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerEnvironmentBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerHooksBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerHooksBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerHooksBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerHooksBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerLoggerBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerLoggerBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerLoggerBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerLoggerBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerOptionsBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerOptionsBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerOptionsBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerOptionsBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerRawBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerRawBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerRawBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerRawBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerSpecializeBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerSpecializeBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerSpecializeBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerSpecializeBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStorageStrategyWithFilter.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStorageStrategyWithFilter.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStorageStrategyWithFilter.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStorageStrategyWithFilter.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStoreStrategyBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerStoreStrategyBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStoreStrategyBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerUriBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerUriBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IProducerUriBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerUriBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IRawProducer.cs similarity index 95% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IRawProducer.cs index 2c4d18bf..81efaedb 100644 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/IRawProducer.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IRawProducer.cs @@ -1,6 +1,4 @@ -using System.Runtime.CompilerServices; - -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone { /// /// Event Source raw producer. diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuildBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideBuildBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuildBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideBuildBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideEnvironmentBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideUriBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideUriBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/Override/IProducerOverrideUriBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/Override/IProducerOverrideUriBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RawProducerOptions.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/RawProducerOptions.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RawProducerOptions.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/RawProducerOptions.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RouteAssignmentType.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/RouteAssignmentType.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/Building/RouteAssignmentType.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/Building/RouteAssignmentType.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Builder/IProducerBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerAsyncInterceptor.cs b/EventSourcing.Backbone.Abstractions/Producer/Interceptors/IProducerAsyncInterceptor.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerAsyncInterceptor.cs rename to EventSourcing.Backbone.Abstractions/Producer/Interceptors/IProducerAsyncInterceptor.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerInterceptor.cs b/EventSourcing.Backbone.Abstractions/Producer/Interceptors/IProducerInterceptor.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/IProducerInterceptor.cs rename to EventSourcing.Backbone.Abstractions/Producer/Interceptors/IProducerInterceptor.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/ProducerInterceptorBridge.cs b/EventSourcing.Backbone.Abstractions/Producer/Interceptors/ProducerInterceptorBridge.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Interceptors/ProducerInterceptorBridge.cs rename to EventSourcing.Backbone.Abstractions/Producer/Interceptors/ProducerInterceptorBridge.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlan.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/IProducerPlan.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlan.cs rename to EventSourcing.Backbone.Abstractions/Producer/Plan/IProducerPlan.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBase.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/IProducerPlanBase.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBase.cs rename to EventSourcing.Backbone.Abstractions/Producer/Plan/IProducerPlanBase.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/IProducerPlanBuilder.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/IProducerPlanBuilder.cs rename to EventSourcing.Backbone.Abstractions/Producer/Plan/IProducerPlanBuilder.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Plan/ProducerPlan.cs rename to EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerExtensions.cs b/EventSourcing.Backbone.Abstractions/Producer/ProducerExtensions.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerExtensions.cs rename to EventSourcing.Backbone.Abstractions/Producer/ProducerExtensions.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerPipeline.cs b/EventSourcing.Backbone.Abstractions/Producer/ProducerPipeline.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/ProducerPipeline.cs rename to EventSourcing.Backbone.Abstractions/Producer/ProducerPipeline.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerChannelProvider.cs b/EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerChannelProvider.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerChannelProvider.cs rename to EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerChannelProvider.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerStorageStrategy.cs b/EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerStorageStrategy.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Provider/IProducerStorageStrategy.cs rename to EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerStorageStrategy.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerAsyncSegmentationStrategy.cs b/EventSourcing.Backbone.Abstractions/Producer/Segmentation/IProducerAsyncSegmentationStrategy.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerAsyncSegmentationStrategy.cs rename to EventSourcing.Backbone.Abstractions/Producer/Segmentation/IProducerAsyncSegmentationStrategy.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerSegmentationStrategy.cs b/EventSourcing.Backbone.Abstractions/Producer/Segmentation/IProducerSegmentationStrategy.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/IProducerSegmentationStrategy.cs rename to EventSourcing.Backbone.Abstractions/Producer/Segmentation/IProducerSegmentationStrategy.cs diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/ProducerSegmentationStrategyBridge.cs b/EventSourcing.Backbone.Abstractions/Producer/Segmentation/ProducerSegmentationStrategyBridge.cs similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/Segmentation/ProducerSegmentationStrategyBridge.cs rename to EventSourcing.Backbone.Abstractions/Producer/Segmentation/ProducerSegmentationStrategyBridge.cs diff --git a/EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs b/EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs deleted file mode 100644 index c016c5e7..00000000 --- a/EventSourcing.Backbone.Abstractions/TelemetryrExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Diagnostics; - -using OpenTelemetry.Trace; - -namespace EventSourcing.Backbone -{ - public static class TelemetryrExtensions - { - /// - /// Adds the event consumer telemetry source (will result in tracing the consumer). - /// - /// The builder. - /// - public static TracerProviderBuilder ListenToEventSourceRedisChannel(this TracerProviderBuilder builder) => - builder.AddSource( - EventSourceConstants.REDIS_CONSUMER_CHANNEL_SOURCE, - EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); - - #region InjectMetaTelemetryTags - - /// - /// Adds standard open-telemetry tags (for redis). - /// - /// The meta. - /// The activity. - public static void InjectMetaTelemetryTags(this Metadata meta, Activity? activity) - { - activity?.SetTag("event-source.uri", meta.Uri); - activity?.SetTag("event-source.operation", meta.Operation); - activity?.SetTag("event-source.message-id", meta.MessageId); - activity?.SetTag("event-source.channel-type", meta.ChannelType); - } - - #endregion // InjectMetaTelemetryTags - } -} diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 97b7b86a..c216b9ad 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -15,10 +15,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Producers", "Producers", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Consumers", "Consumers", "{EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Producers.Abstractions", "Producers\EventSourcing.Backbone.Producers.Abstractions\EventSourcing.Backbone.Producers.Abstractions.csproj", "{1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Consumers.Abstractions", "Consumers\EventSourcing.Backbone.Consumers.Abstractions\EventSourcing.Backbone.Consumers.Abstractions.csproj", "{8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Consumers", "Consumers\EventSourcing.Backbone.Consumers\EventSourcing.Backbone.Consumers.csproj", "{6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Producers", "Producers\EventSourcing.Backbone.Producers\EventSourcing.Backbone.Producers.csproj", "{90B4EBE9-7CDB-42E0-B5E2-9FCAC4AD52CA}" @@ -58,6 +54,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.SrcG EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.SrcGen.Playground", "src-gen\EventSourcing.Backbone.SrcGen.Playground\EventSourcing.Backbone.SrcGen.Playground.csproj", "{49A8B718-DAB6-4D04-874C-0B1EA12826EB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlayGroundAbstraction", "src-gen\PlayGroundAbstraction\PlayGroundAbstraction.csproj", "{FBD76237-D44F-4DC4-BCB1-0DCA85A73052}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -80,16 +78,6 @@ Global {E17F4D78-8645-457E-B4CB-455FCED12F49}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {E17F4D78-8645-457E-B4CB-455FCED12F49}.Release|Any CPU.ActiveCfg = Release|Any CPU {E17F4D78-8645-457E-B4CB-455FCED12F49}.Release|Any CPU.Build.0 = Release|Any CPU - {1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2}.Release|Any CPU.Build.0 = Release|Any CPU - {8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9}.Release|Any CPU.Build.0 = Release|Any CPU {6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106}.Debug|Any CPU.Build.0 = Debug|Any CPU {6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106}.Gen|Any CPU.ActiveCfg = Gen|Any CPU @@ -151,14 +139,18 @@ Global {49A8B718-DAB6-4D04-874C-0B1EA12826EB}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {49A8B718-DAB6-4D04-874C-0B1EA12826EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {49A8B718-DAB6-4D04-874C-0B1EA12826EB}.Release|Any CPU.Build.0 = Release|Any CPU + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Gen|Any CPU.Build.0 = Gen|Any CPU + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {C1618305-4F0B-4AAA-8386-CA31DCBC5F59} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} - {1FBD2827-BDF7-4988-89D0-8FDCF0DF2BC2} = {3FBE7FA1-700D-4B58-8569-15F533F761D1} - {8BDB7BF5-69F3-44B1-BCBE-4A9680215FE9} = {EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2} {6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106} = {EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2} {90B4EBE9-7CDB-42E0-B5E2-9FCAC4AD52CA} = {3FBE7FA1-700D-4B58-8569-15F533F761D1} {64FC6860-BD4C-4C11-BF6E-E99BB4D91723} = {75CE8A6F-D63B-491D-8834-8D3BA2127208} @@ -173,6 +165,7 @@ Global {D648BE46-4208-4DEB-9F2F-0B009B603D0A} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} {230DA8DF-00EE-47E2-A7B8-54F05A8966F6} = {2AE6684B-47A2-46D3-BE4E-C14B05255BB6} {49A8B718-DAB6-4D04-874C-0B1EA12826EB} = {2AE6684B-47A2-46D3-BE4E-C14B05255BB6} + {FBD76237-D44F-4DC4-BCB1-0DCA85A73052} = {2AE6684B-47A2-46D3-BE4E-C14B05255BB6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {103E4CA1-859C-4E6A-9F2D-7FD54A8E687C} diff --git a/PlayGroundConsumer/IFooX.cs b/PlayGroundConsumer/IFooX.cs new file mode 100644 index 00000000..d1258b18 --- /dev/null +++ b/PlayGroundConsumer/IFooX.cs @@ -0,0 +1,7 @@ +namespace PlayGroundAbstraction; + +//[EventsContract(EventsContractType.Consumer)] +public interface IFooX : IFoo +{ + +} \ No newline at end of file diff --git a/PlayGroundConsumer/PlayGroundConsumer.csproj b/PlayGroundConsumer/PlayGroundConsumer.csproj new file mode 100644 index 00000000..e524145e --- /dev/null +++ b/PlayGroundConsumer/PlayGroundConsumer.csproj @@ -0,0 +1,26 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + True + + + + + + + + diff --git a/PlayGroundConsumer/Program.cs b/PlayGroundConsumer/Program.cs new file mode 100644 index 00000000..3751555c --- /dev/null +++ b/PlayGroundConsumer/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/Consumers/EventSourcing.Backbone.Consumers.Abstractions/icon.png b/PlayGroundConsumer/icon.png similarity index 100% rename from Consumers/EventSourcing.Backbone.Consumers.Abstractions/icon.png rename to PlayGroundConsumer/icon.png diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj b/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj deleted file mode 100644 index 183f09da..00000000 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/EventSourcing.Backbone.Producers.Abstractions.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - README.md - - - - - True - \ - - - - - - - - - - - - - diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs b/Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs deleted file mode 100644 index 3337fd6f..00000000 --- a/Producers/EventSourcing.Backbone.Producers.Abstractions/TelemetryrExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics; - -using OpenTelemetry; -using OpenTelemetry.Context.Propagation; - -namespace EventSourcing.Backbone -{ - public static class TelemetryrExtensions - { - private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; - - #region StartSpanScope - - /// - /// Inject telemetry span to the channel property. - /// - /// - /// The activity. - /// The meta. - /// The entries builder. - /// The injection strategy. - /// - public static void InjectSpan( - this Activity? activity, - Metadata meta, - ImmutableArray.Builder entriesBuilder, - Action.Builder, string, string> injectStrategy) - { - // Depending on Sampling (and whether a listener is registered or not), the - // activity above may not be created. - // If it is created, then propagate its context. - // If it is not created, the propagate the Current context, - // if any. - if (activity != null) - { - Activity.Current = activity; - } - ActivityContext contextToInject = Activity.Current?.Context ?? default; - - // Inject the ActivityContext into the message metadata to propagate trace context to the receiving service. - Propagator.Inject(new PropagationContext(contextToInject, Baggage.Current), entriesBuilder, injectStrategy); - } - - #endregion // StartSpanScope - } -} diff --git a/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj b/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj index 53e47b52..18e4efe6 100644 --- a/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj +++ b/Producers/EventSourcing.Backbone.Producers/EventSourcing.Backbone.Producers.csproj @@ -12,7 +12,6 @@ - diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 33291f75..d269009f 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -51,8 +51,8 @@ public EndToEndStressTests( { DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior { - CalcNextDelay =(d => TimeSpan.FromMilliseconds(2)) - } + CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + } }; var consumerBuilder = stg.CreateRedisConsumerBuilder(); consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index 6effa167..0e76bd2b 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -45,9 +45,7 @@ - - diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs index 709544cf..1a3042f8 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs @@ -1,26 +1,11 @@ -using System.Diagnostics; -using System.Text.Json; -using System.Threading.Tasks.Dataflow; - -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Enums; using EventSourcing.Backbone.Tests; using EventSourcing.Backbone.UnitTests.Entities; using FakeItEasy; -using Microsoft.Extensions.Logging; - -using Polly; - -using StackExchange.Redis; - using Xunit; using Xunit.Abstractions; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static EventSourcing.Backbone.EventSourceConstants; - // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest namespace EventSourcing.Backbone.IntegrationTests.HelloWorld diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index a09a9dee..87c01574 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -273,7 +273,7 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - await tcs.Task.WithCancellation(new CancellationTokenSource(TimeSpan.FromSeconds(20)).Token); + await tcs.Task.WithCancellation(new CancellationTokenSource(TimeSpan.FromSeconds(20)).Token); #region Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 1ccd59ff..10632367 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -10,8 +10,6 @@ using Microsoft.Extensions.Logging; -using Polly; - using StackExchange.Redis; using Xunit; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs index 0780ab80..7c9aee56 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs @@ -1,25 +1,16 @@ -using System.Diagnostics; -using System.Text.Json; using System.Threading.Tasks.Dataflow; -using EventSourcing.Backbone.Building; -using EventSourcing.Backbone.Enums; using EventSourcing.Backbone.IntegrationTests.HelloWorld; -using EventSourcing.Backbone.UnitTests.Entities; using FakeItEasy; using Microsoft.Extensions.Logging; -using Polly; - using StackExchange.Redis; -using Xunit; using Xunit.Abstractions; using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static EventSourcing.Backbone.EventSourceConstants; #pragma warning disable S3881 // "IDisposable" should be implemented correctly diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj index 0e17784d..7ede6473 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj @@ -27,9 +27,7 @@ - - diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index ae29be7d..a8f0618e 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -5,8 +5,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; -using Newtonsoft.Json.Serialization; - using OpenTelemetry.Exporter; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -23,8 +21,6 @@ namespace Microsoft.Extensions.Configuration /// public static class AspCoreExtensions { - private static readonly NamingStrategy _namingStrategy = new CamelCaseNamingStrategy(); - #region AddRedis /// @@ -71,7 +67,7 @@ public static IServiceCollection AddOpenTelemetryWeknow( services.AddOpenTelemetry() .WithTracing(builder => { - builder.SetResourceBuilder(ResourceBuilder.CreateDefault() + var tracerProviderBuilder = builder.SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(shortAppName)) .ListenToEventSourceRedisChannel() // .SetSampler() diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj b/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj index 10aba54f..6e9c0cd9 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj @@ -6,13 +6,8 @@ True - - - - - - + diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 9e56c9ca..7b74a79c 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -408,7 +408,12 @@ private static void GenerateProducerMethods( i++; } var classifications = Enumerable.Range(0, prms.Length).Select(m => $"classification{m}"); - builder.AppendLine($"\t\t\treturn await SendAsync(operation, {string.Join(", ", classifications)});"); + + builder.Append($"\t\t\treturn await SendAsync(operation"); + if (classifications.Any()) + builder.AppendLine($", {string.Join(", ", classifications)});"); + else + builder.AppendLine($");"); builder.AppendLine("\t\t}"); builder.AppendLine(); } diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 8bb83a8a..c8b70a85 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -44,6 +44,8 @@ protected override GenInstruction[] OnGenerate( var asm = GetType().Assembly.GetName(); builder.AppendLine($"\t[GeneratedCode(\"{asm.Name}\",\"{asm.Version}\")]"); builder.Append($"\tpublic interface {interfaceName}"); + // TODO: [bnaya 2023-05-23] make sure to generate the base interfaces + // TODO: [bnaya 2023-05-23] SellectMany into interface hierarchic var baseTypes = symbol.Interfaces.Select(m => info.FormatName(m.Name)); string inheritance = string.Join(", ", baseTypes); if (string.IsNullOrEmpty(inheritance)) diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json b/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json index ae5d793d..e5a73ae6 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json +++ b/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Weknow.EventSourcing.Backbone.SrcGen": { "commandName": "DebugRoslynComponent", - "targetProject": "..\\Weknow.EventSourcing.Backbone.SrcGen.Playground\\Weknow.EventSourcing.Backbone.SrcGen.Playground.csproj" + "targetProject": "..\\PlayGroundAbstraction\\PlayGroundAbstraction.csproj" } } } \ No newline at end of file diff --git a/src-gen/PlayGroundAbstraction/IFoo.cs b/src-gen/PlayGroundAbstraction/IFoo.cs new file mode 100644 index 00000000..a8742425 --- /dev/null +++ b/src-gen/PlayGroundAbstraction/IFoo.cs @@ -0,0 +1,10 @@ +using EventSourcing.Backbone; + +namespace PlayGroundAbstraction; + +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +public interface IFoo +{ + ValueTask Run(); +} \ No newline at end of file diff --git a/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj b/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj new file mode 100644 index 00000000..78d905d7 --- /dev/null +++ b/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj @@ -0,0 +1,25 @@ + + + + net7.0 + enable + enable + + + + + + + + + + True + + + + + + + + + diff --git a/Producers/EventSourcing.Backbone.Producers.Abstractions/icon.png b/src-gen/PlayGroundAbstraction/icon.png similarity index 100% rename from Producers/EventSourcing.Backbone.Producers.Abstractions/icon.png rename to src-gen/PlayGroundAbstraction/icon.png diff --git a/src-gen/PlayGroundProducer/IFooY.cs b/src-gen/PlayGroundProducer/IFooY.cs new file mode 100644 index 00000000..decd71dc --- /dev/null +++ b/src-gen/PlayGroundProducer/IFooY.cs @@ -0,0 +1,7 @@ +namespace PlayGroundAbstraction; + +//[EventsContract(EventsContractType.Producer)] +public interface IFooY : IFoo +{ + +} \ No newline at end of file diff --git a/src-gen/PlayGroundProducer/PlayGroundProducer.csproj b/src-gen/PlayGroundProducer/PlayGroundProducer.csproj new file mode 100644 index 00000000..9ca3a8d7 --- /dev/null +++ b/src-gen/PlayGroundProducer/PlayGroundProducer.csproj @@ -0,0 +1,28 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + True + + + + + + + + + + diff --git a/src-gen/PlayGroundProducer/Program.cs b/src-gen/PlayGroundProducer/Program.cs new file mode 100644 index 00000000..3751555c --- /dev/null +++ b/src-gen/PlayGroundProducer/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/src-gen/PlayGroundProducer/icon.png b/src-gen/PlayGroundProducer/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 Date: Wed, 24 May 2023 07:55:41 +0000 Subject: [PATCH 040/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index e69539ee..627fc85e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.40 + 1.2.41 From 84f3593d0799df8bd80c27008b3ae36a6665205e Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 07:56:01 +0000 Subject: [PATCH 041/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 627fc85e..863d8982 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.41 + 1.2.42 @@ -44,7 +44,7 @@ - + From f72526b8afede226284ae7820e93f3569478eb29 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 11:11:54 +0300 Subject: [PATCH 042/178] rfc: cleanup --- .../Attributes/EventSourceVersion.cs | 1 - .../Concrete/BridgeIncrementalGenerator.cs | 29 ++++++------------- .../Concrete/ContractIncrementalGenerator.cs | 6 ++-- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs index 5a23d626..45b847a8 100644 --- a/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs +++ b/EventSourcing.Backbone.Abstractions/Attributes/EventSourceVersion.cs @@ -8,7 +8,6 @@ /// We don't expect a gap between ConsumeFrom to a lower version. /// Versions expect to start at 0, if no version specified It will consider version 0, ///

- /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class EventSourceVersionAttribute : Attribute { diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 7b74a79c..2bdb394b 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -5,7 +5,6 @@ using EventSourcing.Backbone.SrcGen.Generators.EntitiesAndHelpers; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; namespace EventSourcing.Backbone { @@ -28,10 +27,6 @@ protected override GenInstruction[] OnGenerate( { string interfaceName = info.FormatName(); var builder = new StringBuilder(); - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; - - var verrideInterfaceArg = att.ArgumentList?.Arguments.FirstOrDefault(m => m.NameEquals?.Name.Identifier.ValueText == "InterfaceName"); - var overrideInterfaceName = verrideInterfaceArg?.Expression.NormalizeWhitespace().ToString().Replace("\"", ""); if (info.Kind == "Producer") { @@ -83,9 +78,6 @@ protected GenInstruction OnGenerateConsumerBridgeExtensions( AssemblyName assemblyName) { var builder = new StringBuilder(); - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; - - // CopyDocumentation(builder, kind, item, "\t"); string bridge = $"{prefix}Bridge"; string fileName = $"{bridge}Extensions"; @@ -141,9 +133,7 @@ protected GenInstruction OnGenerateConsumerBridge( AssemblyName assemblyName) { var builder = new StringBuilder(); - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; - - // CopyDocumentation(builder, kind, item, "\t"); + var symbol = info.Symbol; string fileName = $"{prefix}Bridge"; @@ -243,9 +233,7 @@ protected GenInstruction OnGenerateConsumerBase( AssemblyName assemblyName) { var builder = new StringBuilder(); - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; - - // CopyDocumentation(builder, kind, item, "\t"); + var symbol = info.Symbol; string fileName = $"{prefix}Base"; @@ -301,10 +289,9 @@ protected GenInstruction OnGenerateConsumerBase( { string mtdName = method.Name; - //CopyDocumentation(builder, kind, mds); var prms = method.Parameters; IEnumerable ps = prms.Select(p => $"{p.Type} {p.Name}"); - builder.AppendLine($"\t\t\tprotected abstract ValueTask {mtdName}({string.Join(", ", ps)});"); ; + builder.AppendLine($"\t\t\tprotected abstract ValueTask {mtdName}({string.Join(", ", ps)});"); builder.AppendLine(); } builder.AppendLine("\t\t}"); @@ -322,10 +309,10 @@ protected string OnGenerateProducer( SyntaxReceiverResult info, string interfaceName) { - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; + var symbol = info.Symbol; + var kind = info.Kind; builder.AppendLine("\tusing EventSourcing.Backbone.Building;"); - // CopyDocumentation(builder, kind, item, "\t"); string prefix = interfaceName.StartsWith("I") && interfaceName.Length > 1 && char.IsUpper(interfaceName[1]) ? interfaceName.Substring(1) : interfaceName; @@ -401,12 +388,14 @@ private static void GenerateProducerMethods( builder.AppendLine($"\t\t\tvar operation = nameof({interfaceName}.{mtdName});"); int i = 0; var prms = mds.Parameters; - foreach (var p in prms) + foreach (var pName in from p in prms + let pName = p.Name + select pName) { - var pName = p.Name; builder.AppendLine($"\t\t\tvar classification{i} = CreateClassificationAdaptor(operation, nameof({pName}), {pName});"); i++; } + var classifications = Enumerable.Range(0, prms.Length).Select(m => $"classification{m}"); builder.Append($"\t\t\treturn await SendAsync(operation"); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index c8b70a85..5d48fc9e 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -25,9 +25,8 @@ public ContractncrementalGenerator() : base(TARGET_ATTRIBUTE) /// Called when [execute]. ///
/// The context. + /// /// The information. - /// Name of the interface. - /// /// /// File name /// @@ -37,7 +36,7 @@ protected override GenInstruction[] OnGenerate( SyntaxReceiverResult info) { - var (type, att, symbol, kind, ns, isProducer) = info; + var (type, att, symbol, kind, _, isProducer) = info; string interfaceName = info.FormatName(); var builder = new StringBuilder(); CopyDocumentation(builder, kind, type, "\t"); @@ -78,7 +77,6 @@ protected override GenInstruction[] OnGenerate( var contractOnlyArg = att.ArgumentList?.Arguments.FirstOrDefault(m => m.NameEquals?.Name.Identifier.ValueText == "ContractOnly"); var contractOnly = contractOnlyArg?.Expression.NormalizeWhitespace().ToString() == "true"; - //info = info.OverrideName(interfaceName); if (!contractOnly) _bridge.GenerateSingle(context, compilation, info); From ca783b004558e56a230894a4ad1e3ef5ed4a8b5a Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 24 May 2023 08:12:25 +0000 Subject: [PATCH 043/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 863d8982..bfe00214 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.42 + 1.2.43 From 8877b15d3e1221b19cf9b32abb9cc90b073c1897 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 08:12:43 +0000 Subject: [PATCH 044/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index bfe00214..715e417e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.43 + 1.2.44 @@ -44,7 +44,7 @@ - + From b909bdefd8bcdbee7e9bd6395d05c8369f3f04c4 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 08:21:56 +0000 Subject: [PATCH 045/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 715e417e..588b4e04 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.44 + 1.2.45 @@ -44,7 +44,7 @@ - + From fe56f734635b4f50e512603d5c9a6f4db6f3002d Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 08:25:55 +0000 Subject: [PATCH 046/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 588b4e04..7eb6a452 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.45 + 1.2.46 @@ -44,7 +44,7 @@ - + From 7845b49d1c7444781e85ea8c7f4885edf2e7fdd7 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 11:29:44 +0300 Subject: [PATCH 047/178] build: fix --- EventSourcing.Backbone.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index c216b9ad..6afeb0f9 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -142,7 +142,6 @@ Global {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Gen|Any CPU.Build.0 = Gen|Any CPU {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection From da75cf5b89353702e895acd8939a54fcb82e5fee Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 24 May 2023 08:30:11 +0000 Subject: [PATCH 048/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7eb6a452..5a1c872a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.46 + 1.2.47 From 36c3868c832a8366f412a67c81125e31b7b67607 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 08:30:27 +0000 Subject: [PATCH 049/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5a1c872a..041b0d05 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.47 + 1.2.48 @@ -44,7 +44,7 @@ - + From 913200be1ac444775a7294c7a758e2a60d04f724 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 11:55:13 +0300 Subject: [PATCH 050/178] ci: fix --- Directory.Build.props | 2 +- .../EventSourcing.Backbone.SrcGen.Playground.csproj | 2 +- src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 041b0d05..8754dcbd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.48 + 1.2.53 diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj b/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj index 6e9c0cd9..a18ce92e 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/EventSourcing.Backbone.SrcGen.Playground.csproj @@ -3,7 +3,7 @@ Exe Debug;Release;Gen - True + False diff --git a/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj b/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj index 78d905d7..706eac63 100644 --- a/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj +++ b/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj @@ -4,6 +4,7 @@ net7.0 enable enable + False From 54b159b4fa5309ebe2a46c5f19c56c14a4dcaa46 Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 24 May 2023 08:55:37 +0000 Subject: [PATCH 051/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8754dcbd..c4b72e07 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.53 + 1.2.54 From 7990ad60e9df389a04e03a89e8a21a3ddfef0d6b Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 08:55:55 +0000 Subject: [PATCH 052/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index c4b72e07..e47cddac 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.54 + 1.2.55 @@ -44,7 +44,7 @@ - + From 005c48b6c1583a260ff58d52fa610c17ac6ff4c5 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 08:58:56 +0000 Subject: [PATCH 053/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index e47cddac..6ea758cb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.55 + 1.2.56 @@ -44,7 +44,7 @@ - + From 451b01f272bfec829ef918f4dbb03a0883017965 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 13:52:19 +0300 Subject: [PATCH 054/178] feat: less sleeping --- .../RedisConsumerChannel.cs | 2 +- .../Setting/DelayWhenEmptyBehavior.cs | 14 +++++++++++--- .../Builder/ConsumerBase.EventSourceSubscriber.cs | 2 -- .../EndToEndStressTests.cs | 2 +- .../EndToEndTests.cs | 2 +- .../InheritanceTests.cs | 2 +- .../MigrationTest.cs | 2 +- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 9f50a106..b9e280a3 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -991,7 +991,7 @@ private async ValueTask GetBucketAsync( private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationToken cancellationToken) { var cfg = _setting.DelayWhenEmptyBehavior; - var newDelay = cfg.CalcNextDelay(previousDelay); + var newDelay = cfg.CalcNextDelay(previousDelay, cfg); var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); newDelay = TimeSpan.FromMilliseconds(limitDelay); await Task.Delay(newDelay, cancellationToken); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs index b0b0a764..0b9936eb 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Setting/DelayWhenEmptyBehavior.cs @@ -8,6 +8,7 @@ namespace EventSourcing.Backbone.Channels.RedisProvider ///
public record DelayWhenEmptyBehavior { + private const int MIN_DELAY_MILLI = 2; public static readonly DelayWhenEmptyBehavior Default = new DelayWhenEmptyBehavior(); /// @@ -15,21 +16,28 @@ public record DelayWhenEmptyBehavior /// public TimeSpan MaxDelay { get; init; } = TimeSpan.FromSeconds(5); + /// + /// The increment factor when increasing the delay (hang on empty). + /// The previous delay will multiply by this factor + Ceiling to endure increment. + /// + public double DelayFactor { get; init; } = 1.2; + /// /// Gets or sets the next delay. /// - public Func CalcNextDelay { get; init; } = DefaultCalcNextDelay; + public Func CalcNextDelay { get; init; } = DefaultCalcNextDelay; /// /// Default calculation of next delay. /// /// The previous delay. + /// The setting. /// - private static TimeSpan DefaultCalcNextDelay(TimeSpan previous) + private static TimeSpan DefaultCalcNextDelay(TimeSpan previous, DelayWhenEmptyBehavior setting) { var prevMilli = previous.TotalMilliseconds; - var milli = Max(prevMilli * 2, 10); + var milli = Max(Math.Ceiling(prevMilli * setting.DelayFactor), MIN_DELAY_MILLI); return TimeSpan.FromMilliseconds(milli); } } diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index c7584daa..b8ac23c0 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -134,7 +134,6 @@ private async ValueTask ConsumingAsync( var consumerMeta = new ConsumerMetadata(meta, cancellation); var logger = Logger; - logger.LogDebug("Consuming event: {0}", meta.FullUri()); #region _plan.Interceptors.InterceptAsync(...) @@ -178,7 +177,6 @@ private async ValueTask ConsumingAsync( } return false; }, cancellation); - logger.LogDebug("Consumed event: {0}", meta.FullUri()); var options = Plan.Options; var behavior = options.AckBehavior; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index d269009f..1873fce6 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -51,7 +51,7 @@ public EndToEndStressTests( { DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + CalcNextDelay = ((d, _) => TimeSpan.FromMilliseconds(2)) } }; var consumerBuilder = stg.CreateRedisConsumerBuilder(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 4eb9d890..f3d5ce64 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -74,7 +74,7 @@ public EndToEndTests( { DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + CalcNextDelay = ((d, _) => TimeSpan.FromMilliseconds(2)) } }; var consumerBuilder = stg.CreateRedisConsumerBuilder(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index 87c01574..f7b7f353 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -63,7 +63,7 @@ public InheritanceTests( { DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + CalcNextDelay = ((d, _) => TimeSpan.FromMilliseconds(2)) } }; var consumerBuilder = stg.CreateRedisConsumerBuilder(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 10632367..b85540fc 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -55,7 +55,7 @@ public MigrationTest( { DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior { - CalcNextDelay = (d => TimeSpan.FromMilliseconds(2)) + CalcNextDelay = ((d, _) => TimeSpan.FromMilliseconds(2)) } }; _consumerBuilder = stg.CreateRedisConsumerBuilder(); From dc7bb1bcd96d3783a33cfbef00ec6c7bc1895b75 Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 24 May 2023 11:06:05 +0000 Subject: [PATCH 055/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 6ea758cb..63a18dd5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.56 + 1.2.57 From 651810124d77285d912b52fb42f07d5f98a7e429 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 11:06:21 +0000 Subject: [PATCH 056/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 63a18dd5..4be6af20 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.57 + 1.2.58 @@ -44,7 +44,7 @@ - + From 3a6cc09cd763f4fecb7e8c0a12af3e86fd35fe70 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 15:53:36 +0300 Subject: [PATCH 057/178] feat: recovery --- .../RedisConsumerChannel.cs | 126 ++++++++++++------ EventSourcing.Backbone.sln | 27 ++++ .../ConsumingEvents/ConsumingEvents.csproj | 23 ++++ Tests/HelloWorld/ConsumingEvents/Program.cs | 39 ++++++ Tests/HelloWorld/ConsumingEvents/icon.png | Bin 0 -> 58881 bytes .../EventsAbstractions.csproj | 24 ++++ .../EventsAbstractions/IHelloEvents.cs | 17 +++ Tests/HelloWorld/EventsAbstractions/URIs.cs | 13 ++ Tests/HelloWorld/EventsAbstractions/icon.png | Bin 0 -> 58881 bytes .../ProducingEvents/ProducingEvents.csproj | 23 ++++ Tests/HelloWorld/ProducingEvents/Program.cs | 54 ++++++++ Tests/HelloWorld/ProducingEvents/icon.png | Bin 0 -> 58881 bytes 12 files changed, 304 insertions(+), 42 deletions(-) create mode 100644 Tests/HelloWorld/ConsumingEvents/ConsumingEvents.csproj create mode 100644 Tests/HelloWorld/ConsumingEvents/Program.cs create mode 100644 Tests/HelloWorld/ConsumingEvents/icon.png create mode 100644 Tests/HelloWorld/EventsAbstractions/EventsAbstractions.csproj create mode 100644 Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs create mode 100644 Tests/HelloWorld/EventsAbstractions/URIs.cs create mode 100644 Tests/HelloWorld/EventsAbstractions/icon.png create mode 100644 Tests/HelloWorld/ProducingEvents/ProducingEvents.csproj create mode 100644 Tests/HelloWorld/ProducingEvents/Program.cs create mode 100644 Tests/HelloWorld/ProducingEvents/icon.png diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index b9e280a3..ed250033 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -457,16 +457,29 @@ async Task ReadBatchAsync() IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); + try + { + values = await db.StreamReadGroupAsync( + key, + plan.ConsumerGroup, + plan.ConsumerName, + position: StreamPosition.NewMessages, + count: bachSize, + flags: flags) + .WithCancellation(ct, () => Array.Empty()) + .WithCancellation(cancellationToken, () => Array.Empty()); + } + #region Exception Handling - values = await db.StreamReadGroupAsync( - key, - plan.ConsumerGroup, - plan.ConsumerName, - position: StreamPosition.NewMessages, - count: bachSize, - flags: flags) - .WithCancellation(ct, () => Array.Empty()) - .WithCancellation(cancellationToken, () => Array.Empty()); + catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) + { + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger); + } + + #endregion // Exception Handling } StreamEntry[] results = values ?? Array.Empty(); return results; @@ -503,30 +516,45 @@ async Task ReadSelfPending() IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); - StreamPendingMessageInfo[] pendMsgInfo = await db.StreamPendingMessagesAsync( - key, - plan.ConsumerGroup, - options.BatchSize, - plan.ConsumerName, - flags: CommandFlags.DemandMaster); - if (pendMsgInfo != null && pendMsgInfo.Length != 0) + try { - var ids = pendMsgInfo - .Select(m => m.MessageId).ToArray(); - if (ids.Length != 0) + StreamPendingMessageInfo[] pendMsgInfo = await db.StreamPendingMessagesAsync( + key, + plan.ConsumerGroup, + options.BatchSize, + plan.ConsumerName, + flags: CommandFlags.DemandMaster); + if (pendMsgInfo != null && pendMsgInfo.Length != 0) { - values = await db.StreamClaimAsync(key, - plan.ConsumerGroup, - plan.ConsumerName, - 0, - ids, - flags: CommandFlags.DemandMaster); - values = values ?? Array.Empty(); - _logger.LogInformation("Claimed messages: {ids}", ids); + var ids = pendMsgInfo + .Select(m => m.MessageId).ToArray(); + if (ids.Length != 0) + { + values = await db.StreamClaimAsync(key, + plan.ConsumerGroup, + plan.ConsumerName, + 0, + ids, + flags: CommandFlags.DemandMaster); + values = values ?? Array.Empty(); + _logger.LogInformation("Claimed messages: {ids}", ids); + } } + + return values; + } + #region Exception Handling + + catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) + { + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger); + return Array.Empty(); } - return values; + #endregion // Exception Handling } #endregion // ReadSelfPending @@ -683,20 +711,34 @@ async Task ReleaseAsync(RedisValue[] freeTargets) { IConnectionMultiplexer conn = await _connFactory.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); - await db.StreamClaimAsync(plan.FullUri(), - plan.ConsumerGroup, - RedisChannelConstants.NONE_CONSUMER, - 1, - freeTargets, - flags: CommandFlags.DemandMaster); - await Task.Delay(releaseDelay, cancellationToken); - if (releaseDelay < MAX_RELEASE_DELAY) - releaseDelay = Math.Min(releaseDelay * 2, MAX_RELEASE_DELAY); - - if (bachSize == options.BatchSize) - bachSize = 1; - else - bachSize = Math.Min(bachSize * 2, options.BatchSize); + try + { + await db.StreamClaimAsync(plan.FullUri(), + plan.ConsumerGroup, + RedisChannelConstants.NONE_CONSUMER, + 1, + freeTargets, + flags: CommandFlags.DemandMaster); + await Task.Delay(releaseDelay, cancellationToken); + if (releaseDelay < MAX_RELEASE_DELAY) + releaseDelay = Math.Min(releaseDelay * 2, MAX_RELEASE_DELAY); + + if (bachSize == options.BatchSize) + bachSize = 1; + else + bachSize = Math.Min(bachSize * 2, options.BatchSize); + } + #region Exception Handling + + catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) + { + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger); + } + + #endregion // Exception Handling } #endregion // ReleaseAsync diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 6afeb0f9..b2b17bcb 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -56,6 +56,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.SrcG EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlayGroundAbstraction", "src-gen\PlayGroundAbstraction\PlayGroundAbstraction.csproj", "{FBD76237-D44F-4DC4-BCB1-0DCA85A73052}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HelloWorld", "HelloWorld", "{9A1275C5-2299-4560-ABB6-CE56B9175CF7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventsAbstractions", "Tests\HelloWorld\EventsAbstractions\EventsAbstractions.csproj", "{81EBE4CC-2E70-4E66-89DC-DCBB591343AB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsumingEvents", "Tests\HelloWorld\ConsumingEvents\ConsumingEvents.csproj", "{A78DF256-6896-4246-8CF5-8CCBB27760A5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProducingEvents", "Tests\HelloWorld\ProducingEvents\ProducingEvents.csproj", "{B528D83E-1AB9-4461-BA5E-993FF882F85B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -144,6 +152,21 @@ Global {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBD76237-D44F-4DC4-BCB1-0DCA85A73052}.Release|Any CPU.Build.0 = Release|Any CPU + {81EBE4CC-2E70-4E66-89DC-DCBB591343AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81EBE4CC-2E70-4E66-89DC-DCBB591343AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81EBE4CC-2E70-4E66-89DC-DCBB591343AB}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {81EBE4CC-2E70-4E66-89DC-DCBB591343AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81EBE4CC-2E70-4E66-89DC-DCBB591343AB}.Release|Any CPU.Build.0 = Release|Any CPU + {A78DF256-6896-4246-8CF5-8CCBB27760A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A78DF256-6896-4246-8CF5-8CCBB27760A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A78DF256-6896-4246-8CF5-8CCBB27760A5}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {A78DF256-6896-4246-8CF5-8CCBB27760A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A78DF256-6896-4246-8CF5-8CCBB27760A5}.Release|Any CPU.Build.0 = Release|Any CPU + {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -165,6 +188,10 @@ Global {230DA8DF-00EE-47E2-A7B8-54F05A8966F6} = {2AE6684B-47A2-46D3-BE4E-C14B05255BB6} {49A8B718-DAB6-4D04-874C-0B1EA12826EB} = {2AE6684B-47A2-46D3-BE4E-C14B05255BB6} {FBD76237-D44F-4DC4-BCB1-0DCA85A73052} = {2AE6684B-47A2-46D3-BE4E-C14B05255BB6} + {9A1275C5-2299-4560-ABB6-CE56B9175CF7} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} + {81EBE4CC-2E70-4E66-89DC-DCBB591343AB} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} + {A78DF256-6896-4246-8CF5-8CCBB27760A5} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} + {B528D83E-1AB9-4461-BA5E-993FF882F85B} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {103E4CA1-859C-4E6A-9F2D-7FD54A8E687C} diff --git a/Tests/HelloWorld/ConsumingEvents/ConsumingEvents.csproj b/Tests/HelloWorld/ConsumingEvents/ConsumingEvents.csproj new file mode 100644 index 00000000..ff03128c --- /dev/null +++ b/Tests/HelloWorld/ConsumingEvents/ConsumingEvents.csproj @@ -0,0 +1,23 @@ + + + + Exe + false + + + + + + + + + + + True + + + + + + + diff --git a/Tests/HelloWorld/ConsumingEvents/Program.cs b/Tests/HelloWorld/ConsumingEvents/Program.cs new file mode 100644 index 00000000..4cfa5b73 --- /dev/null +++ b/Tests/HelloWorld/ConsumingEvents/Program.cs @@ -0,0 +1,39 @@ +using EventsAbstractions; + +using EventSourcing.Backbone; +using StackExchange.Redis; +using System; +using System.Collections.Concurrent; +using System.Drawing; + +Console.WriteLine("Consuming Events"); + +IConsumerLifetime subscription = RedisConsumerBuilder.Create() + .Uri(URIs.Default) + .Group("sample.hello-world") + .SubscribeHelloEventsConsumer(Subscription.Instance); +Console.ReadKey(false); + +class Subscription : IHelloEventsConsumer +{ + public static readonly Subscription Instance = new Subscription(); + public ValueTask NameAsync(string name) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(); + Console.Write($"Hello {name}: "); + return ValueTask.CompletedTask; + } + + public ValueTask ColorAcync(ConsoleColor color) + { + Console.ForegroundColor = color; + return ValueTask.CompletedTask; + } + + public ValueTask StarAsync() + { + Console.Write("✱"); + return ValueTask.CompletedTask; + } +} diff --git a/Tests/HelloWorld/ConsumingEvents/icon.png b/Tests/HelloWorld/ConsumingEvents/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 + + false + + + + + + + + + + + + + + True + + + + + + + diff --git a/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs b/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs new file mode 100644 index 00000000..b8623466 --- /dev/null +++ b/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs @@ -0,0 +1,17 @@ +using EventSourcing.Backbone; + +namespace EventsAbstractions; + + +///

+/// Event's schema definition +/// Return type of each method should be +/// +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +public interface IHelloEvents +{ + ValueTask NameAsync(string name); + ValueTask ColorAcync(ConsoleColor color); + ValueTask StarAsync(); +} \ No newline at end of file diff --git a/Tests/HelloWorld/EventsAbstractions/URIs.cs b/Tests/HelloWorld/EventsAbstractions/URIs.cs new file mode 100644 index 00000000..c63b3b67 --- /dev/null +++ b/Tests/HelloWorld/EventsAbstractions/URIs.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EventsAbstractions +{ + public class URIs + { + public const string Default = "hello.event-sourcing"; + } +} diff --git a/Tests/HelloWorld/EventsAbstractions/icon.png b/Tests/HelloWorld/EventsAbstractions/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 + + + Exe + false + + + + + + + + + + + True + + + + + + + diff --git a/Tests/HelloWorld/ProducingEvents/Program.cs b/Tests/HelloWorld/ProducingEvents/Program.cs new file mode 100644 index 00000000..6080b7da --- /dev/null +++ b/Tests/HelloWorld/ProducingEvents/Program.cs @@ -0,0 +1,54 @@ +using EventsAbstractions; + +using EventSourcing.Backbone; + +using System; + +Console.WriteLine("Producing events"); + +IHelloEventsProducer producer = RedisProducerBuilder.Create() + .Uri(URIs.Default) + .BuildHelloEventsProducer(); + + + +Console.Write("What is your name? "); +string name = Console.ReadLine(); +await producer.NameAsync(name ?? "Unknown"); + +var rnd = new Random(Guid.NewGuid().GetHashCode()); +Console.WriteLine("Press Esc to exit"); +Console.Write("Press Number for delay: "); +Console.WriteLine("1 is ms, 2 is 10 ms, 3 is 100 ms, 4 is 1s, 5 is 10s, 6 is 30s, 7 is 1m"); + + +var colors = Enum.GetValues() ?? Array.Empty(); +while (!Console.KeyAvailable || Console.ReadKey(true).Key == ConsoleKey.Escape) +{ + int index = Environment.TickCount % colors.Length; + var color = colors[index]; + await producer.ColorAcync(color); + await producer.StarAsync(); + + ConsoleKey press = Console.KeyAvailable ? Console.ReadKey(true).Key : ConsoleKey.Clear; + int delay = press switch + { + ConsoleKey.D1 => 1, + ConsoleKey.D2 => 10, + ConsoleKey.D3 => 100, + ConsoleKey.D4 => 1_000, + ConsoleKey.D5 => 10_000, + ConsoleKey.D6 => 30_000, + ConsoleKey.D7 => 60_000, + ConsoleKey.Escape => -1, + _ => 0 + }; + if (delay == -1) + break; + if (delay != 0) + await Task.Delay(delay); + Console.ForegroundColor = color; + Console.Write("☆"); +} + +Console.WriteLine(" Done"); \ No newline at end of file diff --git a/Tests/HelloWorld/ProducingEvents/icon.png b/Tests/HelloWorld/ProducingEvents/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 Date: Wed, 24 May 2023 12:54:11 +0000 Subject: [PATCH 058/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 4be6af20..447402bf 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.58 + 1.2.59 From 8e939b3d496325a92b4a335fd3181221863b1c55 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 12:54:29 +0000 Subject: [PATCH 059/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 447402bf..3f584f67 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.59 + 1.2.60 @@ -44,7 +44,7 @@ - + From cf87fc8dd87d68e29f7c269deb8a0bddeed24299 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 17:01:24 +0300 Subject: [PATCH 060/178] feat: default consumer group --- .../EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index ab48a4fa..55479f7a 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -244,7 +244,7 @@ IConsumerChannelProvider IConsumerPlan.Channel /// Consumer Group allow a group of clients to cooperate /// consuming a different portion of the same stream of messages ///

- public string ConsumerGroup { get; } = string.Empty; + public string ConsumerGroup { get; } = "default"; #endregion // ConsumerGroup From 0d95b7fc0163a9b5dda7c9f9d72f8ba5e365af94 Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 24 May 2023 14:01:47 +0000 Subject: [PATCH 061/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3f584f67..5cc7a66f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.60 + 1.2.61 From 692e8add3bed90cc52046a6139428026c0971604 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 14:02:04 +0000 Subject: [PATCH 062/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5cc7a66f..1e7edc9c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.61 + 1.2.62 @@ -44,7 +44,7 @@ - + From da0da4bdd8d47d166a4af9b85bc7121719f4c63f Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 24 May 2023 17:04:21 +0300 Subject: [PATCH 063/178] test: remove group --- Tests/HelloWorld/ConsumingEvents/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/HelloWorld/ConsumingEvents/Program.cs b/Tests/HelloWorld/ConsumingEvents/Program.cs index 4cfa5b73..71ac93da 100644 --- a/Tests/HelloWorld/ConsumingEvents/Program.cs +++ b/Tests/HelloWorld/ConsumingEvents/Program.cs @@ -10,7 +10,7 @@ IConsumerLifetime subscription = RedisConsumerBuilder.Create() .Uri(URIs.Default) - .Group("sample.hello-world") + //.Group("sample.hello-world") .SubscribeHelloEventsConsumer(Subscription.Instance); Console.ReadKey(false); From ca3b53d506c913d2614438b859f587cdf9b3f71a Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 24 May 2023 14:04:43 +0000 Subject: [PATCH 064/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1e7edc9c..c89901f7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.62 + 1.2.63 From eea215094c3968cdcda7ddfefc2b1f3f20c39635 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Wed, 24 May 2023 14:05:03 +0000 Subject: [PATCH 065/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index c89901f7..b3215240 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.63 + 1.2.64 @@ -44,7 +44,7 @@ - + From 614754b519617341e71a4f243c999eda8feaf473 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 28 May 2023 11:52:50 +0300 Subject: [PATCH 066/178] fix: namespace generation bug --- .../Generators/GeneratorIncrementalBase.cs | 30 +++++++++---------- src-gen/PlayGroundAbstraction/IFoo.cs | 15 ++++++---- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs index a57c6f54..9d4f7ea4 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs @@ -177,21 +177,21 @@ public void GenerateSingle( usingSet.Add(u); } - var overrideNS = dynamicNs ?? ns; - if (overrideNS == null && item.Parent is NamespaceDeclarationSyntax ns_) - { - foreach (var c in ns_?.Parent?.ChildNodes() ?? Array.Empty()) - { - if (c is UsingDirectiveSyntax use) - { - var u = use.ToFullString().Trim(); - if (!usingSet.Contains(u)) - usingSet.Add(u); - } - } - builder.AppendLine(); - overrideNS = ns_?.Name?.ToString(); - } + var overrideNS = dynamicNs ?? ns ?? att.ContainingNamespace.Name; + //if (overrideNS == null && item.Parent is BaseNamespaceDeclarationSyntax ns_) + //{ + // foreach (var c in ns_?.Parent?.ChildNodes() ?? Array.Empty()) + // { + // if (c is UsingDirectiveSyntax use) + // { + // var u = use.ToFullString().Trim(); + // if (!usingSet.Contains(u)) + // usingSet.Add(u); + // } + // } + // builder.AppendLine(); + // overrideNS = ns_?.Name?.ToString(); + //} foreach (var u in usingSet.OrderBy(m => m)) { builder.AppendLine(u); diff --git a/src-gen/PlayGroundAbstraction/IFoo.cs b/src-gen/PlayGroundAbstraction/IFoo.cs index a8742425..8b0d330c 100644 --- a/src-gen/PlayGroundAbstraction/IFoo.cs +++ b/src-gen/PlayGroundAbstraction/IFoo.cs @@ -1,10 +1,13 @@ using EventSourcing.Backbone; -namespace PlayGroundAbstraction; - -[EventsContract(EventsContractType.Producer)] -[EventsContract(EventsContractType.Consumer)] -public interface IFoo +namespace PlayGroundAbstraction { - ValueTask Run(); + + + [EventsContract(EventsContractType.Producer)] + [EventsContract(EventsContractType.Consumer)] + public interface IFoo + { + ValueTask Run(); + } } \ No newline at end of file From f93e098b0b0871fff859235d4a74200701b7b043 Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 28 May 2023 08:53:12 +0000 Subject: [PATCH 067/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index b3215240..65db1d3b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.64 + 1.2.65 From fe4538f7038c7c4d5d37a0a48c884f39de864c7f Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 28 May 2023 08:53:28 +0000 Subject: [PATCH 068/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 65db1d3b..72fd7dfa 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.65 + 1.2.66 @@ -44,7 +44,7 @@ - + From 999415e7850ade7f2b3d063ba195dbb7b0cd7bb0 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 28 May 2023 14:13:58 +0300 Subject: [PATCH 069/178] fix: generation of namespace & using --- .../InheritanceTests.cs | 1 + .../Concrete/BridgeIncrementalGenerator.cs | 29 ++++++--- .../Concrete/ContractIncrementalGenerator.cs | 9 ++- .../EntitiesAndHelpers/EntityGenerator.cs | 5 +- .../Generators/GeneratorIncrementalBase.cs | 15 ++--- .../RoslynHelper.cs | 33 +++++++++- .../SyntaxReceiverResult.cs | 62 ++++++++++++++++++- src-gen/PlayGroundAbstraction/IFoo.cs | 17 ++--- src-gen/PlayGroundAbstraction/User.cs | 7 +++ 9 files changed, 144 insertions(+), 34 deletions(-) create mode 100644 src-gen/PlayGroundAbstraction/User.cs diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index f7b7f353..646cf4f9 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -11,6 +11,7 @@ using FakeItEasy; using Microsoft.Extensions.Logging; +using EventSourcing.Backbone.UnitTests.Entities; using StackExchange.Redis; diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 2bdb394b..d2b91740 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -23,17 +23,18 @@ public BridgeIncrementalGenerator() : base(TARGET_ATTRIBUTE) protected override GenInstruction[] OnGenerate( SourceProductionContext context, Compilation compilation, - SyntaxReceiverResult info) + SyntaxReceiverResult info, + string[] usingStatements) { string interfaceName = info.FormatName(); var builder = new StringBuilder(); if (info.Kind == "Producer") { - var file = OnGenerateProducer(builder, info, interfaceName); + var file = OnGenerateProducer(builder, info, interfaceName, usingStatements); return new[] { new GenInstruction(file, builder.ToString()) }; } - return OnGenerateConsumers(info, interfaceName); + return OnGenerateConsumers(info, interfaceName, usingStatements); } @@ -42,8 +43,9 @@ protected override GenInstruction[] OnGenerate( #region OnGenerateConsumers protected GenInstruction[] OnGenerateConsumers( - SyntaxReceiverResult info, - string interfaceName) + SyntaxReceiverResult info, + string interfaceName, + string[] usingStatements) { string generateFrom = info.FormatName(); string prefix = (info.Name ?? interfaceName).StartsWith("I") && @@ -51,6 +53,7 @@ protected GenInstruction[] OnGenerateConsumers( char.IsUpper(interfaceName[1]) ? interfaceName.Substring(1) : interfaceName; AssemblyName assemblyName = GetType().Assembly.GetName(); + string ns = info.Symbol.ContainingNamespace.ToDisplayString(); var dtos = EntityGenerator.GenerateEntities(prefix, info, interfaceName, generateFrom, assemblyName); GenInstruction[] gens = @@ -242,7 +245,6 @@ protected GenInstruction OnGenerateConsumerBase( builder.AppendLine("\t\t/// "); builder.AppendLine($"\t\t/// Base Subscription class of {interfaceName}"); builder.AppendLine("\t\t/// "); - builder.AppendLine($"\t\t[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]"); builder.AppendLine($"\t\t[GeneratedCode(\"{assemblyName.Name}\",\"{assemblyName.Version}\")]"); builder.AppendLine($"\t\tpublic abstract class {fileName}: ISubscriptionBridge"); builder.AppendLine("\t\t{"); @@ -305,13 +307,22 @@ protected GenInstruction OnGenerateConsumerBase( #region OnGenerateProducer protected string OnGenerateProducer( - StringBuilder builder, - SyntaxReceiverResult info, - string interfaceName) + StringBuilder builder, + SyntaxReceiverResult info, + string interfaceName, + string[] usingStatements) { var symbol = info.Symbol; var kind = info.Kind; + var hash = new HashSet(); + hash.Add("using EventSourcing.Backbone.Building;"); builder.AppendLine("\tusing EventSourcing.Backbone.Building;"); + foreach (var u in usingStatements) + { + if (hash.Contains(u)) + continue; + builder.AppendLine(u); + } string prefix = interfaceName.StartsWith("I") && interfaceName.Length > 1 && diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 5d48fc9e..50caef75 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -33,10 +33,11 @@ public ContractncrementalGenerator() : base(TARGET_ATTRIBUTE) protected override GenInstruction[] OnGenerate( SourceProductionContext context, Compilation compilation, - SyntaxReceiverResult info) + SyntaxReceiverResult info, + string[] usingStatements) { - var (type, att, symbol, kind, _, isProducer) = info; + var (type, att, symbol, kind, ns, isProducer, @using) = info; string interfaceName = info.FormatName(); var builder = new StringBuilder(); CopyDocumentation(builder, kind, type, "\t"); @@ -65,7 +66,7 @@ protected override GenInstruction[] OnGenerate( builder.Append(""); builder.Append($" {mds.Identifier.ValueText}("); - var ps = mds.ParameterList.Parameters.Select(p => $"\r\n\t\t\t{p.Type} {p.Identifier.ValueText}"); + var ps = mds.ParameterList.Parameters.Select(GetParameter); builder.Append("\t\t\t"); builder.Append(string.Join(", ", ps)); builder.AppendLine(");"); @@ -81,6 +82,8 @@ protected override GenInstruction[] OnGenerate( _bridge.GenerateSingle(context, compilation, info); return new[] { new GenInstruction(interfaceName, builder.ToString()) }; + + string GetParameter(ParameterSyntax p) => $"\r\n\t\t\t{p.Type} {p.Identifier.ValueText}"; } } } diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs index 56c526aa..936a3310 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs @@ -32,7 +32,7 @@ internal static GenInstruction[] GenerateEntities( AssemblyName assemblyName) { - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; + var (item, symbol, kind, ns, usingStatements) = info; var results = new List(); foreach (var method in item.Members) @@ -106,7 +106,7 @@ internal static GenInstruction GenerateEntityMapper( AssemblyName assemblyName) { var builder = new StringBuilder(); - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; + var (item, symbol, kind, ns, @using) = info; // CopyDocumentation(builder, kind, item, "\t"); @@ -208,7 +208,6 @@ internal static GenInstruction GenerateEntityMapperExtensions( AssemblyName assemblyName) { var builder = new StringBuilder(); - var (item, att, symbol, name, kind, suffix, ns, isProducer) = info; // CopyDocumentation(builder, kind, item, "\t"); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs index 9d4f7ea4..3b806871 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/GeneratorIncrementalBase.cs @@ -149,7 +149,7 @@ public void GenerateSingle( Compilation compilation, SyntaxReceiverResult info) { - var (item, symbol, att, name, kind, suffix, ns, isProducer) = info; + var (item, symbol, kind, ns, usingStatements) = info; #region Validation @@ -161,7 +161,7 @@ public void GenerateSingle( #endregion // Validation - GenInstruction[] codes = OnGenerate(context, compilation, info); + GenInstruction[] codes = OnGenerate(context, compilation, info, usingStatements); foreach (var (fileName, content, dynamicNs, usn) in codes) { @@ -171,13 +171,13 @@ public void GenerateSingle( builder.AppendLine(); builder.AppendLine("#nullable enable"); - foreach (var u in usn) + foreach (var u in usingStatements.Concat(usn)) { if (!usingSet.Contains(u)) usingSet.Add(u); } - var overrideNS = dynamicNs ?? ns ?? att.ContainingNamespace.Name; + var overrideNS = dynamicNs ?? ns ?? symbol.ContainingNamespace.ToDisplayString(); //if (overrideNS == null && item.Parent is BaseNamespaceDeclarationSyntax ns_) //{ // foreach (var c in ns_?.Parent?.ChildNodes() ?? Array.Empty()) @@ -213,16 +213,17 @@ public void GenerateSingle( /// Called when [execute]. ///
/// The context. + /// The compilation. /// The information. - /// Name of the interface. - /// Source of the generation. + /// The using statements. /// /// File name /// protected abstract GenInstruction[] OnGenerate( SourceProductionContext context, Compilation compilation, - SyntaxReceiverResult info); + SyntaxReceiverResult info, + string[] usingStatements); #endregion // OnGenerate diff --git a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs index 1e8a07a8..05b82bba 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs @@ -1,4 +1,6 @@ -namespace Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis; internal static class RoslynHelper { @@ -66,4 +68,33 @@ public static IEnumerable GetAllMethods(this ITypeSymbol type) } #endregion // GetAllMethods + + #region GetUsing + + /// + /// Gets the using statements. + /// + /// The syntax node. + /// + public static IEnumerable GetUsing(this SyntaxNode syntaxNode) + { + if (syntaxNode is CompilationUnitSyntax m) + { + foreach (var u in m.Usings) + { + var match = u.ToString(); + yield return match; + } + } + + if(syntaxNode == null) + yield break; + + foreach (var u in GetUsing(syntaxNode.Parent)) + { + yield return u; + } + } + + #endregion // GetUsing } diff --git a/src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs b/src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs index 21ab341d..125588bf 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/SyntaxReceiverResult.cs @@ -46,10 +46,11 @@ public SyntaxReceiverResult( Type = type; Kind = kind; Name = name; - Namespace = ns; + Namespace = ns ?? symbol?.ContainingNamespace?.ToDisplayString(); Att = att; Symbol = symbol ?? throw new NullReferenceException(nameof(symbol)); GenerateFrom = type.Identifier.ValueText; + Using = att.GetUsing().ToArray(); } /// @@ -100,6 +101,11 @@ public SyntaxReceiverResult( /// public INamedTypeSymbol Symbol { get; } + /// + /// Gets the using statements. + /// + public string[] Using { get; } + #region FormatName public string FormatName() @@ -120,6 +126,52 @@ public string FormatName(string generateFrom) #region Deconstruct + /// + /// Deconstruct the specified type. + /// + /// The type. + /// The attribute. + /// The symbol. + /// The kind. + /// The namespace. + /// The using statement. + public void Deconstruct(out TypeDeclarationSyntax type, + out INamedTypeSymbol symbol, + out string kind, + out string? ns, + out string[] usingStatement) + { + type = Type; + symbol = Symbol; + kind = Kind; + ns = Namespace; + usingStatement = Using; + } + + /// + /// Deconstruct the specified type. + /// + /// The type. + /// The attribute. + /// The symbol. + /// The kind. + /// The namespace. + /// The using statement. + public void Deconstruct(out TypeDeclarationSyntax type, + out AttributeSyntax att, + out INamedTypeSymbol symbol, + out string kind, + out string? ns, + out string[] usingStatement) + { + type = Type; + symbol = Symbol; + att = Att; + kind = Kind; + ns = Namespace; + usingStatement = Using; + } + /// /// Deconstruct the specified type. /// @@ -135,7 +187,8 @@ public void Deconstruct(out TypeDeclarationSyntax type, out INamedTypeSymbol symbol, out string kind, out string? ns, - out bool isProducer) + out bool isProducer, + out string[] usingStatement) { type = Type; symbol = Symbol; @@ -143,6 +196,7 @@ public void Deconstruct(out TypeDeclarationSyntax type, kind = Kind; ns = Namespace; isProducer = IsProducer; + usingStatement = Using; } /// @@ -162,7 +216,8 @@ public void Deconstruct(out TypeDeclarationSyntax type, out string kind, out string suffix, out string? ns, - out bool isProducer) + out bool isProducer, + out string[] usingStatement) { type = Type; symbol = Symbol; @@ -172,6 +227,7 @@ public void Deconstruct(out TypeDeclarationSyntax type, suffix = Suffix; ns = Namespace; isProducer = IsProducer; + usingStatement = Using; } #endregion // Deconstruct diff --git a/src-gen/PlayGroundAbstraction/IFoo.cs b/src-gen/PlayGroundAbstraction/IFoo.cs index 8b0d330c..0573edc2 100644 --- a/src-gen/PlayGroundAbstraction/IFoo.cs +++ b/src-gen/PlayGroundAbstraction/IFoo.cs @@ -1,13 +1,14 @@ using EventSourcing.Backbone; -namespace PlayGroundAbstraction -{ +using PlayGroundAbstraction.Common.Local; + +namespace PlayGroundAbstraction.Common; - [EventsContract(EventsContractType.Producer)] - [EventsContract(EventsContractType.Consumer)] - public interface IFoo - { - ValueTask Run(); - } + +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +public interface IFoo +{ + ValueTask Run(User user); } \ No newline at end of file diff --git a/src-gen/PlayGroundAbstraction/User.cs b/src-gen/PlayGroundAbstraction/User.cs new file mode 100644 index 00000000..1eba984c --- /dev/null +++ b/src-gen/PlayGroundAbstraction/User.cs @@ -0,0 +1,7 @@ +namespace PlayGroundAbstraction.Common.Local +{ + public record User + { + public required string Name { get; init; } + } +} \ No newline at end of file From 37c6822d2aead4121bc64db0f984fa4cda1a50ee Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 28 May 2023 11:14:38 +0000 Subject: [PATCH 070/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 72fd7dfa..a2d30436 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.66 + 1.2.67 From 7ca834cbe3ae080bb85da6af080b1de856455f47 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 28 May 2023 11:14:57 +0000 Subject: [PATCH 071/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a2d30436..3aae8938 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.67 + 1.2.68 @@ -44,7 +44,7 @@ - + From b51ed324a49cec4399ab05c67e0c7eeeff2c9c2c Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 28 May 2023 15:15:19 +0300 Subject: [PATCH 072/178] fix: iterative on empty when continue to listen --- .../RedisConsumerChannel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index ed250033..30db6dab 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -962,6 +962,7 @@ async IAsyncEnumerable AsyncLoop() { if (options?.ExitWhenEmpty ?? true) yield break; delay = await DelayIfEmpty(delay, cancellationToken); + continue; } string k = string.Empty; foreach (StreamEntry e in entries) From 057ace372b0bf88d52b511f172a2ee8c8dc879e9 Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 28 May 2023 12:15:40 +0000 Subject: [PATCH 073/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3aae8938..8630658a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.68 + 1.2.69 From 28cfdd1969b05d6dd6baf5c437d662cf90f4ca8e Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 28 May 2023 12:15:56 +0000 Subject: [PATCH 074/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8630658a..64c643f6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.69 + 1.2.70 @@ -44,7 +44,7 @@ - + From 40ff731ffec0240fd95f605a6dcb9321259a37a6 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 28 May 2023 15:40:56 +0300 Subject: [PATCH 075/178] build: nuget update --- ...ourcing.Backbone.Channels.RedisConsumerProvider.csproj | 2 +- ...Sourcing.Backbone.Channels.RedisProvider.Common.csproj | 2 +- ...rcing.Backbone.Channels.S3StoreConsumerProvider.csproj | 2 +- ...urcing.Backbone.Channels.S3StoreProvider.Common.csproj | 2 +- .../EventSourcing.Backbone.Abstractions.csproj | 2 +- EventSourcing.Backbone/EventSourcing.Backbone.csproj | 2 +- .../EventSourcing.Backbone.IntegrationTests.csproj | 4 ++-- .../EventSourcing.Backbone.UnitTests.csproj | 8 ++++---- .../EventSourcing.Backbone.WebEventTest.csproj | 2 +- .../EventSourcing.Backbone.SrcGen.csproj | 2 +- .../PlayGroundAbstraction/PlayGroundAbstraction.csproj | 5 +++++ 11 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj index 63b66eca..f82ec8f1 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj @@ -20,7 +20,7 @@ - + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj index 35d76463..ccdc75dd 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj @@ -16,7 +16,7 @@ - + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj index b2435eb0..95d2c152 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj @@ -15,7 +15,7 @@ - + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index 948ba2c3..6f928f34 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index 571b322d..173ca42a 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -17,7 +17,7 @@ - + diff --git a/EventSourcing.Backbone/EventSourcing.Backbone.csproj b/EventSourcing.Backbone/EventSourcing.Backbone.csproj index e31dfeaf..fe568197 100644 --- a/EventSourcing.Backbone/EventSourcing.Backbone.csproj +++ b/EventSourcing.Backbone/EventSourcing.Backbone.csproj @@ -20,7 +20,7 @@ - + diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index 0e76bd2b..6db0e9c1 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -27,14 +27,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj index 7ede6473..59f6fae2 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj @@ -8,21 +8,21 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index f5cb31a8..5c863870 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -19,7 +19,7 @@ - + diff --git a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj index 4dc9f3eb..68f70643 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj +++ b/src-gen/EventSourcing.Backbone.SrcGen/EventSourcing.Backbone.SrcGen.csproj @@ -27,7 +27,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj b/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj index 706eac63..4a003241 100644 --- a/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj +++ b/src-gen/PlayGroundAbstraction/PlayGroundAbstraction.csproj @@ -23,4 +23,9 @@ + + + + + From 431f693e8d7dbac4f44fc72b7ad5953dc226f226 Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 28 May 2023 12:41:20 +0000 Subject: [PATCH 076/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 64c643f6..25774691 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.70 + 1.2.71 From 8e95f7246752fc8282912eef03fdec56ab13d1b2 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 28 May 2023 12:41:37 +0000 Subject: [PATCH 077/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 25774691..458b5221 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.71 + 1.2.72 @@ -44,7 +44,7 @@ - + From c5818b354c812e4f7a1d478c739b6fb62c73e684 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 28 May 2023 18:21:37 +0300 Subject: [PATCH 078/178] feat: Async suffix convention --- Tests/HelloWorld/ConsumingEvents/Program.cs | 2 +- .../EventsAbstractions/IHelloEvents.cs | 2 +- Tests/HelloWorld/ProducingEvents/Program.cs | 2 +- .../Concrete/BridgeIncrementalGenerator.cs | 8 ++++---- .../Concrete/ContractIncrementalGenerator.cs | 2 +- .../EntitiesAndHelpers/EntityGenerator.cs | 4 ++-- .../RoslynHelper.cs | 20 +++++++++++++++++++ 7 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Tests/HelloWorld/ConsumingEvents/Program.cs b/Tests/HelloWorld/ConsumingEvents/Program.cs index 71ac93da..ae6713f0 100644 --- a/Tests/HelloWorld/ConsumingEvents/Program.cs +++ b/Tests/HelloWorld/ConsumingEvents/Program.cs @@ -25,7 +25,7 @@ public ValueTask NameAsync(string name) return ValueTask.CompletedTask; } - public ValueTask ColorAcync(ConsoleColor color) + public ValueTask ColorAsync(ConsoleColor color) { Console.ForegroundColor = color; return ValueTask.CompletedTask; diff --git a/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs b/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs index b8623466..4e23e044 100644 --- a/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs +++ b/Tests/HelloWorld/EventsAbstractions/IHelloEvents.cs @@ -12,6 +12,6 @@ namespace EventsAbstractions; public interface IHelloEvents { ValueTask NameAsync(string name); - ValueTask ColorAcync(ConsoleColor color); + ValueTask ColorAsync(ConsoleColor color); ValueTask StarAsync(); } \ No newline at end of file diff --git a/Tests/HelloWorld/ProducingEvents/Program.cs b/Tests/HelloWorld/ProducingEvents/Program.cs index 6080b7da..7d37c076 100644 --- a/Tests/HelloWorld/ProducingEvents/Program.cs +++ b/Tests/HelloWorld/ProducingEvents/Program.cs @@ -27,7 +27,7 @@ { int index = Environment.TickCount % colors.Length; var color = colors[index]; - await producer.ColorAcync(color); + await producer.ColorAsync(color); await producer.StarAsync(); ConsoleKey press = Console.KeyAvailable ? Console.ReadKey(true).Key : ConsoleKey.Clear; diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 64afa999..bf13fe5b 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -193,7 +193,7 @@ protected GenInstruction OnGenerateConsumerBridge( builder.AppendLine("\t\t\t{"); foreach (var method in allMethods) { - string mtdName = method.Name; + string mtdName = method.ToNameConvention(); string mtdType = method.ContainingType.Name; mtdType = info.FormatName(mtdType); builder.AppendLine($"\t\t\t\tcase nameof({mtdType}.{mtdName}):"); @@ -261,7 +261,7 @@ protected GenInstruction OnGenerateConsumerBase( builder.AppendLine("\t\t\t\t{"); foreach (var method in allMethods) { - string mtdName = method.Name; + string mtdName = method.ToNameConvention(); string mtdType = method.ContainingType.Name; mtdType = info.FormatName(mtdType); builder.AppendLine($"\t\t\t\t\tcase nameof({mtdType}.{mtdName}):"); @@ -289,7 +289,7 @@ protected GenInstruction OnGenerateConsumerBase( builder.AppendLine(); foreach (var method in allMethods) { - string mtdName = method.Name; + string mtdName = method.ToNameConvention(); var prms = method.Parameters; IEnumerable ps = prms.Select(p => $"{p.Type} {p.Name}"); @@ -384,7 +384,7 @@ private static void GenerateProducerMethods( SyntaxReceiverResult info, IMethodSymbol mds) { - string mtdName = mds.Name; + string mtdName = mds.ToNameConvention(); string interfaceName = mds.ContainingType.Name; interfaceName = info.FormatName(interfaceName); builder.Append("\t\tasync ValueTask"); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 50caef75..09927358 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -64,7 +64,7 @@ protected override GenInstruction[] OnGenerate( builder.Append("\t\tValueTask"); if (isProducer) builder.Append(""); - builder.Append($" {mds.Identifier.ValueText}("); + builder.Append($" {mds.ToNameConvention()}("); var ps = mds.ParameterList.Parameters.Select(GetParameter); builder.Append("\t\t\t"); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs index 936a3310..c8231b85 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs @@ -46,7 +46,7 @@ internal static GenInstruction[] GenerateEntities( if (recordPrefix.EndsWith(nameof(KindFilter.Consumer))) recordPrefix = recordPrefix.Substring(0, recordPrefix.Length - nameof(KindFilter.Consumer).Length); - string mtdName = mds.Identifier.ValueText; + string mtdName = mds.ToNameConvention(); if (mtdName.EndsWith("Async")) mtdName = mtdName.Substring(0, mtdName.Length - 5); builder.AppendLine($"\t[GeneratedCode(\"{assemblyName.Name}\",\"{assemblyName.Version}\")]"); @@ -159,7 +159,7 @@ internal static GenInstruction GenerateEntityMapper( { if (method is not MethodDeclarationSyntax mds) continue; - string mtdName = mds.Identifier.ValueText; + string mtdName = mds.ToNameConvention(); string recordSuffix = mtdName.EndsWith("Async") ? mtdName.Substring(0, mtdName.Length - 5) : mtdName; string fullRecordName = $"{recordPrefix}_{recordSuffix}"; diff --git a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs index 05b82bba..9a1d8797 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs @@ -97,4 +97,24 @@ public static IEnumerable GetUsing(this SyntaxNode syntaxNode) } #endregion // GetUsing + + #region ToNameConvention + + public static string ToNameConvention(this MethodDeclarationSyntax method) + { + string name = method.Identifier.ValueText; + if (name.EndsWith("Async")) + return name; + return $"{name}Async"; + } + + public static string ToNameConvention(this IMethodSymbol method) + { + string name = method.Name; + if (name.EndsWith("Async")) + return name; + return $"{name}Async"; + } + + #endregion // ToNameConvention } From 42e395c86b1df1fc802b36b5d52f2d7b68c6b82b Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 28 May 2023 15:21:57 +0000 Subject: [PATCH 079/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 458b5221..b3a7f7fc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.72 + 1.2.73 From 4caffa847104138058ecfe8d80fd7430928effb0 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Sun, 28 May 2023 15:22:14 +0000 Subject: [PATCH 080/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index b3a7f7fc..f20aff0b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.73 + 1.2.74 @@ -44,7 +44,7 @@ - + From 60fa7c05f10f31211b30f22d3ae475156503333b Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 29 May 2023 16:08:02 +0300 Subject: [PATCH 081/178] rfc: clean-up dependencies --- ...ntSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj index 95d2c152..051b3d11 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj @@ -14,9 +14,6 @@ - - - From b748bd62c817388a5e3c82b7880a02470c35c97d Mon Sep 17 00:00:00 2001 From: bnayae Date: Mon, 29 May 2023 13:08:24 +0000 Subject: [PATCH 082/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f20aff0b..1e339de8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.74 + 1.2.75 From 21f6da131b35b1a9e35b1a8f5957d4b93ec768b5 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Mon, 29 May 2023 13:08:39 +0000 Subject: [PATCH 083/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1e339de8..f9190088 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.75 + 1.2.76 @@ -44,7 +44,7 @@ - + From 1be14a22de0a5e3b280042f97ecf9fd840f7c4d2 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 31 May 2023 08:38:32 +0300 Subject: [PATCH 084/178] feat: enable more S3 creation options --- .../S3EnvironmentConvention.cs | 2 +- .../S3Options.cs | 5 +- .../S3RepositoryFactory.cs | 100 ++++++++++++------ .../EndToEndExplicitTests.cs | 2 +- .../EndToEndStressTests.cs | 2 +- .../EndToEndTests.cs | 8 +- .../InheritanceTests.cs | 4 +- .../MigrationTest.cs | 1 - 8 files changed, 82 insertions(+), 42 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs index 6560025f..a1fe9441 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3EnvironmentConvention.cs @@ -10,7 +10,7 @@ public enum S3EnvironmentConvention /// None, /// - /// Environment as bucket perfix + /// Environment as bucket prefix /// BucketPrefix, /// diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs index f0fc97c3..f4a91a11 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs @@ -1,4 +1,6 @@ -namespace EventSourcing.Backbone +using Amazon.S3; + +namespace EventSourcing.Backbone { /// /// S3 provider options @@ -17,6 +19,7 @@ public S3Options() /// public string? Bucket { get; init; } + /// /// Base path /// diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index 5ef5a82f..4e0257cd 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using Amazon; +using Amazon.Runtime; using Amazon.S3; using Microsoft.Extensions.Logging; @@ -23,57 +24,94 @@ public sealed class S3RepositoryFactory : IS3RepositoryFactory /// Creates the specified logger. /// /// The logger. - /// The environment variable of access key. - /// The environment variable of secret key. - /// The environment variable of region. + /// The access key or environment variable which hold it. + /// The secret or environment variable which hold it . + /// The region environment variable which hold it. + /// if set to true [will try to find the value from environment variable. /// public static IS3RepositoryFactory Create(ILogger logger, - string envAccessKey = "S3_EVENT_SOURCE_ACCESS_KEY", - string envSecretKey = "S3_EVENT_SOURCE_SECRET", - string envRegion = "S3_EVENT_SOURCE_REGION") => new S3RepositoryFactory(logger, envAccessKey, envSecretKey, envRegion); + string accessKey = "S3_EVENT_SOURCE_ACCESS_KEY", + string secret = "S3_EVENT_SOURCE_SECRET", + string region = "S3_EVENT_SOURCE_REGION", + bool fromEnvironment = true) + { + accessKey = + fromEnvironment + ? Environment.GetEnvironmentVariable(accessKey) ?? accessKey + : accessKey; + secret = + fromEnvironment + ? Environment.GetEnvironmentVariable(secret) ?? secret + : secret; + string? regionKey = + fromEnvironment + ? Environment.GetEnvironmentVariable(region) ?? region + : region; - #endregion // Create + RegionEndpoint rgnKey = (!string.IsNullOrEmpty(regionKey)) + ? RegionEndpoint.GetBySystemName(regionKey) + : RegionEndpoint.USEast2; + var client = new AmazonS3Client(accessKey, secret, rgnKey); + return new S3RepositoryFactory(logger, client); + } - #region Ctor + /// + /// Creates the specified logger. + /// + /// The logger. + /// The credentials. + /// + public static IS3RepositoryFactory Create(ILogger logger, + AWSCredentials credentials) + { + var client = new AmazonS3Client(credentials); + return new S3RepositoryFactory(logger, client); + } /// - /// Initializes a new instance. + /// Creates the specified logger. /// /// The logger. - public S3RepositoryFactory( - ILogger logger) : this((ILogger)logger) + /// The credentials. + /// The configuration. + /// + public static IS3RepositoryFactory Create(ILogger logger, + AWSCredentials credentials, + AmazonS3Config configuration) { + var client = new AmazonS3Client(credentials, configuration); + return new S3RepositoryFactory(logger, client); } + /// + /// Creates the specified logger. + /// + /// The logger. + /// The configuration. + /// + public static IS3RepositoryFactory Create(ILogger logger, + AmazonS3Config configuration) + { + var client = new AmazonS3Client(configuration); + return new S3RepositoryFactory(logger, client); + } + + #endregion // Create + + #region Ctor + /// /// Initializes a new instance. /// /// The logger. - /// The environment variable of access key. - /// The environment variable of secret key. - /// The environment variable of region. + /// The client. public S3RepositoryFactory( ILogger logger, - string envAccessKey = "S3_EVENT_SOURCE_ACCESS_KEY", - string envSecretKey = "S3_EVENT_SOURCE_SECRET", - string envRegion = "S3_EVENT_SOURCE_REGION") + AmazonS3Client client) { _logger = logger; - string accessKey = - Environment.GetEnvironmentVariable(envAccessKey) ?? ""; - string secretKey = - Environment.GetEnvironmentVariable(envSecretKey) ?? ""; - string? regionKey = - Environment.GetEnvironmentVariable(envRegion); - RegionEndpoint rgnKey = (!string.IsNullOrEmpty(regionKey)) - ? RegionEndpoint.GetBySystemName(regionKey) - : RegionEndpoint.USEast2; - - _client = new AmazonS3Client( - accessKey, - secretKey, - rgnKey); + _client = client; } #endregion // Ctor diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 292d06c4..10b7cbe4 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -33,7 +33,7 @@ public class EndToEndExplicitTests : IDisposable private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerStoreStrategyBuilder _consumerBuilder; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 1873fce6..6b53008a 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -30,7 +30,7 @@ public class EndToEndStressTests : IDisposable private readonly IConsumerHooksBuilder _consumerBuilder; private readonly string ENV = $"test"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1000 * 30; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index f3d5ce64..67a835a9 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -46,8 +46,8 @@ public class EndToEndTests : IDisposable private readonly IEventFlowStage1Consumer _stage1Consumer = A.Fake(); private readonly IEventFlowStage2Consumer _stage2Consumer = A.Fake(); - private readonly string ENV = $"Development"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; + private readonly string ENV = $"test"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; @@ -1347,7 +1347,7 @@ public async Task Override_Test() ISequenceOperationsProducer producer = producerBuilder.BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerPrefix = producerBuilder .Specialize() - .Environment("dev") + .Environment("test-override") .Uri("p0.") .BuildSequenceOperationsProducer(); ISequenceOperationsProducer producerPrefix1 = producerBuilder @@ -1392,7 +1392,7 @@ public async Task Override_Test() await using IConsumerLifetime subscriptionPrefix = _consumerBuilder .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnSucceed)) .WithCancellation(cancellation) - .Environment("dev") + .Environment("test-override") .Uri($"p0.{URI}") .WithLogger(_fakeLogger) .Group("CONSUMER_GROUP_1") diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index 646cf4f9..f3a6bd17 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -37,8 +37,8 @@ public class InheritanceTests : IDisposable private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerStoreStrategyBuilder _consumerBuilder; - private readonly string ENV = $"Development"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}:some-shard-{DateTime.UtcNow.Second}"; + private readonly string ENV = $"test"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1_000 * 50; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index b85540fc..90bcac62 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -33,7 +33,6 @@ public class MigrationTest : IDisposable private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"Development"; private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly string SHARD = $"some-shard-{DateTime.UtcNow.Second}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; From 0476deccebd4f87498809ba172fa1f4f8fe18985 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 31 May 2023 12:49:50 +0300 Subject: [PATCH 085/178] feat: use IAmazonS3 --- .../S3ConsumerStorageStrategyExtension.cs | 93 +++++++++++++++- .../S3ProducerStorageStrategyExtension.cs | 102 +++++++++++++++++- .../S3Repository.cs | 4 +- .../S3RepositoryFactory.cs | 2 +- 4 files changed, 192 insertions(+), 9 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 5163b7d2..9863cf90 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -1,4 +1,7 @@  +using Amazon.Runtime; +using Amazon.S3; + using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels; @@ -18,9 +21,97 @@ public static class S3ConsumerStorageStrategyExtension /// The builder. /// The options. /// Type of the target. + /// Either the access key or environment variable hold it (depend on the fromEnvironment parameters). + /// Either the secret or environment variable hold it (depend on the fromEnvironment parameters) + /// Either the region or environment variable hold it (depend on the fromEnvironment parameters) + /// if set to truelooks for the access key, secret and region in the environment variables. + /// + public static IConsumerStoreStrategyBuilder AddS3Strategy( + this IConsumerStoreStrategyBuilder builder, + S3Options options = default, + EventBucketCategories targetType = EventBucketCategories.All, + string envAccessKey = "S3_EVENT_SOURCE_ACCESS_KEY", + string envSecretKey = "S3_EVENT_SOURCE_SECRET", + string envRegion = "S3_EVENT_SOURCE_REGION", + bool fromEnvironment = true) + { + var result = builder.AddStorageStrategyFactory(Local, targetType); + + ValueTask Local(ILogger logger) + { + var factory = S3RepositoryFactory.Create(logger, envAccessKey, envSecretKey, envRegion, fromEnvironment); + var repo = factory.Get(options); + var strategy = new S3ConsumerStorageStrategy(repo); + return strategy.ToValueTask(); + } + return result; + } + + /// + /// Adds the S3 storage strategy. + /// + /// The builder. + /// The credentials. + /// The options. + /// Type of the target. + /// + public static IConsumerStoreStrategyBuilder AddS3Strategy( + this IConsumerStoreStrategyBuilder builder, + AWSCredentials credentials, + S3Options options = default, + EventBucketCategories targetType = EventBucketCategories.All) + { + var result = builder.AddStorageStrategyFactory(Local, targetType); + + ValueTask Local(ILogger logger) + { + var factory = S3RepositoryFactory.Create(logger, credentials); + var repo = factory.Get(options); + var strategy = new S3ConsumerStorageStrategy(repo); + return strategy.ToValueTask(); + } + return result; + } + + /// + /// Adds the S3 storage strategy. + /// + /// The builder. + /// The credentials. + /// The configuration. + /// The options. + /// Type of the target. + /// + public static IConsumerStoreStrategyBuilder AddS3Strategy( + this IConsumerStoreStrategyBuilder builder, + AWSCredentials credentials, + AmazonS3Config configuration, + S3Options options = default, + EventBucketCategories targetType = EventBucketCategories.All) + { + var result = builder.AddStorageStrategyFactory(Local, targetType); + + ValueTask Local(ILogger logger) + { + var factory = S3RepositoryFactory.Create(logger, credentials, configuration); + var repo = factory.Get(options); + var strategy = new S3ConsumerStorageStrategy(repo); + return strategy.ToValueTask(); + } + return result; + } + + /// + /// Adds the S3 storage strategy. + /// + /// The builder. + /// The configuration. + /// The options. + /// Type of the target. /// public static IConsumerStoreStrategyBuilder AddS3Strategy( this IConsumerStoreStrategyBuilder builder, + AmazonS3Config configuration, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All) { @@ -28,7 +119,7 @@ public static IConsumerStoreStrategyBuilder AddS3Strategy( ValueTask Local(ILogger logger) { - var factory = S3RepositoryFactory.Create(logger); + var factory = S3RepositoryFactory.Create(logger, configuration); var repo = factory.Get(options); var strategy = new S3ConsumerStorageStrategy(repo); return strategy.ToValueTask(); diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index bf503ed1..7ecdc776 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -1,4 +1,7 @@  +using Amazon.Runtime; +using Amazon.S3; + using EventSourcing.Backbone.Channels; using Microsoft.Extensions.Logging; @@ -18,9 +21,10 @@ public static class S3ProducerStorageStrategyExtension /// The options. /// Type of the target. /// The filter of which keys in the bucket will be store into this storage. - /// The environment variable of access key. - /// The environment variable of secret key. - /// The environment variable of region. + /// Either the access key or environment variable hold it (depend on the fromEnvironment parameters). + /// Either the secret or environment variable hold it (depend on the fromEnvironment parameters) + /// Either the region or environment variable hold it (depend on the fromEnvironment parameters) + /// if set to truelooks for the access key, secret and region in the environment variables. /// public static IProducerStoreStrategyBuilder AddS3Strategy( this IProducerStoreStrategyBuilder builder, @@ -29,13 +33,74 @@ public static IProducerStoreStrategyBuilder AddS3Strategy( Predicate? filter = null, string envAccessKey = "S3_EVENT_SOURCE_ACCESS_KEY", string envSecretKey = "S3_EVENT_SOURCE_SECRET", - string envRegion = "S3_EVENT_SOURCE_REGION") + string envRegion = "S3_EVENT_SOURCE_REGION", + bool fromEnvironment = true) + { + var result = builder.AddStorageStrategy(Local, targetType, filter); + + ValueTask Local(ILogger logger) + { + var factory = S3RepositoryFactory.Create(logger, envAccessKey, envSecretKey, envRegion, fromEnvironment); + var repo = factory.Get(options); + var strategy = new S3ProducerStorageStrategy(repo); + return strategy.ToValueTask(); + } + + return result; + } + + /// + /// Adds the S3 storage strategy. + /// + /// The builder. + /// The credentials. + /// The configuration. + /// The options. + /// Type of the target. + /// The filter of which keys in the bucket will be store into this storage. + /// + public static IProducerStoreStrategyBuilder AddS3Strategy( + this IProducerStoreStrategyBuilder builder, + AWSCredentials credentials, + AmazonS3Config configuration, + S3Options options = default, + EventBucketCategories targetType = EventBucketCategories.All, + Predicate? filter = null) + { + var result = builder.AddStorageStrategy(Local, targetType, filter); + + ValueTask Local(ILogger logger) + { + var factory = S3RepositoryFactory.Create(logger, credentials, configuration); + var repo = factory.Get(options); + var strategy = new S3ProducerStorageStrategy(repo); + return strategy.ToValueTask(); + } + + return result; + } + + /// + /// Adds the S3 storage strategy. + /// + /// The builder. + /// The configuration. + /// The options. + /// Type of the target. + /// The filter of which keys in the bucket will be store into this storage. + /// + public static IProducerStoreStrategyBuilder AddS3Strategy( + this IProducerStoreStrategyBuilder builder, + AmazonS3Config configuration, + S3Options options = default, + EventBucketCategories targetType = EventBucketCategories.All, + Predicate? filter = null) { var result = builder.AddStorageStrategy(Local, targetType, filter); ValueTask Local(ILogger logger) { - var factory = S3RepositoryFactory.Create(logger, envAccessKey, envSecretKey, envRegion); + var factory = S3RepositoryFactory.Create(logger, configuration); var repo = factory.Get(options); var strategy = new S3ProducerStorageStrategy(repo); return strategy.ToValueTask(); @@ -44,6 +109,33 @@ ValueTask Local(ILogger logger) return result; } + /// + /// Adds the S3 storage strategy. + /// + /// The builder. + /// The credentials. + /// The options. + /// Type of the target. + /// The filter of which keys in the bucket will be store into this storage. + /// + public static IProducerStoreStrategyBuilder AddS3Strategy( + this IProducerStoreStrategyBuilder builder, + AWSCredentials credentials, + S3Options options = default, + EventBucketCategories targetType = EventBucketCategories.All, + Predicate? filter = null) + { + var result = builder.AddStorageStrategy(Local, targetType, filter); + ValueTask Local(ILogger logger) + { + var factory = S3RepositoryFactory.Create(logger, credentials); + var repo = factory.Get(options); + var strategy = new S3ProducerStorageStrategy(repo); + return strategy.ToValueTask(); + } + + return result; + } } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index 71308216..0104386e 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -24,7 +24,7 @@ public sealed class S3Repository : IS3Repository, IDisposable private readonly string _bucket; private readonly ILogger _logger; private readonly string? _basePath; - private readonly AmazonS3Client _client; + private readonly IAmazonS3 _client; private static readonly List EMPTY_TAGS = new List(); private int _disposeCount = 0; private readonly S3EnvironmentConvention _environmentConvension; @@ -40,7 +40,7 @@ public sealed class S3Repository : IS3Repository, IDisposable /// The logger. /// The s3 options. public S3Repository( - AmazonS3Client client, + IAmazonS3 client, ILogger logger, S3Options options = default) { diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index 4e0257cd..a99b6986 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -15,7 +15,7 @@ namespace EventSourcing.Backbone.Channels public sealed class S3RepositoryFactory : IS3RepositoryFactory { private readonly ILogger _logger; - private readonly AmazonS3Client _client; + private readonly IAmazonS3 _client; private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); #region Create From 48766f325bcc8cf83f7352a58a0ad81d3b9cd4c6 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 1 Jun 2023 12:56:47 +0300 Subject: [PATCH 086/178] feat: improve api --- .../RedisConsumerBuilder.cs | 33 +++-- .../RedisConsumerChannel.cs | 10 +- .../RedisHashStorageStrategy.cs | 4 +- .../RedisProducerBuilder.cs | 33 +++-- .../RedisProducerChannel.cs | 4 +- .../RedisHashStorageStrategy.cs | 4 +- .../EventSourceRedisConnectionFacroty.cs | 74 ----------- ...s => EventSourceRedisConnectionFactory.cs} | 125 +++++++++++------- ... => IEventSourceRedisConnectionFactory.cs} | 2 +- .../Factory/RedisCredentialsEnvKeys.cs | 12 +- .../RedisCommonProviderExtensions.cs | 5 +- .../RedisDiExtensions.cs | 62 ++++++++- ...ne.Channels.S3StoreConsumerProvider.csproj | 4 + .../S3ConsumerStorageStrategyExtension.cs | 57 ++------ ...ne.Channels.S3StoreProducerProvider.csproj | 4 + .../S3ProducerStorageStrategyExtension.cs | 59 ++------- .../S3Repository.cs | 6 +- .../S3RepositoryFactory.cs | 38 +----- .../Builder/ConsumerBuilder.cs | 28 +++- .../Builder/ConsumerPlan.cs | 18 ++- .../IConsumerIocStoreStrategyBuilder.cs | 14 ++ .../Building/IConsumerStoreStrategyBuilder.cs | 2 +- .../Consumer/Builder/IConsumerBuilder.cs | 12 +- .../IProducerIocStoreStrategyBuilder.cs | 14 ++ .../Building/IProducerStoreStrategyBuilder.cs | 2 +- .../Producer/Builder/IProducerBuilder.cs | 14 +- .../Producer/Plan/ProducerPlan.cs | 18 ++- .../Builder/ProducerBuilder.cs | 26 +++- .../Contracts/IS3Test.cs | 13 ++ .../Controllers/TestController.cs | 4 +- ...EventSourcing.Backbone.WebEventTest.csproj | 1 + .../Program.cs | 30 +---- .../RegisterEventSourceExtensions.cs | 15 ++- .../appsettings.json | 6 +- Tests/HelloWorld/ProducingEvents/Program.cs | 1 + 35 files changed, 408 insertions(+), 346 deletions(-) delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs rename Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/{RedisConnectionFacrotyBase.cs => EventSourceRedisConnectionFactory.cs} (70%) rename Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/{IEventSourceRedisConnectionFacroty.cs => IEventSourceRedisConnectionFactory.cs} (87%) create mode 100644 EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerIocStoreStrategyBuilder.cs create mode 100644 EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerIocStoreStrategyBuilder.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs index e7fcd7eb..12649be9 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs @@ -172,7 +172,7 @@ IConsumerChannelProvider LocalCreate(ILogger logger) /// internal static IConsumerStoreStrategyBuilder UseRedisChannel( this IConsumerBuilder builder, - IEventSourceRedisConnectionFacroty redisClientFactory, + IEventSourceRedisConnectionFactory redisClientFactory, RedisConsumerChannelSetting? setting = null) { var channelBuilder = builder.UseChannel(LocalCreate); @@ -196,15 +196,25 @@ IConsumerChannelProvider LocalCreate(ILogger logger) /// The setting. /// /// redisClient - public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( + public static IConsumerIocStoreStrategyBuilder ResolveRedisConsumerChannel( this IConsumerBuilder builder, IServiceProvider serviceProvider, RedisConsumerChannelSetting? setting = null) { - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - return builder.UseRedisChannel(connFactory, setting); + var channelBuilder = builder.UseChannel(serviceProvider, LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) + { + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFactory)} is not registered, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + var channel = new RedisConsumerChannel( + connFactory, + logger, + setting); + return channel; + } } /// @@ -214,17 +224,12 @@ public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( /// The setting. /// /// redisClient - public static IConsumerStoreStrategyBuilder UseRedisChannelInjection( + public static IConsumerIocStoreStrategyBuilder ResolveRedisConsumerChannel( this IServiceProvider serviceProvider, RedisConsumerChannelSetting? setting = null) { - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - - var builder = ConsumerBuilder.Empty; - return builder.UseRedisChannel(connFactory, setting); + var result = ConsumerBuilder.Empty.ResolveRedisConsumerChannel(serviceProvider, setting); + return result; } - } } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 30db6dab..811e7090 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -38,7 +38,7 @@ internal class RedisConsumerChannel : IConsumerChannelProvider private readonly ILogger _logger; private readonly RedisConsumerChannelSetting _setting; - private readonly IEventSourceRedisConnectionFacroty _connFactory; + private readonly IEventSourceRedisConnectionFactory _connFactory; private readonly IConsumerStorageStrategy _defaultStorageStrategy; private const string META_SLOT = "____"; private const int INIT_RELEASE_DELAY = 100; @@ -53,7 +53,7 @@ internal class RedisConsumerChannel : IConsumerChannelProvider /// The logger. /// The setting. public RedisConsumerChannel( - IEventSourceRedisConnectionFacroty redisConnFactory, + IEventSourceRedisConnectionFactory redisConnFactory, ILogger logger, RedisConsumerChannelSetting? setting = null) { @@ -73,7 +73,7 @@ public RedisConsumerChannel( ILogger logger, ConfigurationOptions? configuration = null, RedisConsumerChannelSetting? setting = null) : this( - new EventSourceRedisConnectionFacroty( + EventSourceRedisConnectionFactory.Create( logger, configuration), logger, @@ -93,7 +93,7 @@ public RedisConsumerChannel( IRedisCredentials credentialsKeys, RedisConsumerChannelSetting? setting = null, Action? configurationHook = null) : this( - new EventSourceRedisConnectionFacroty( + EventSourceRedisConnectionFactory.Create( credentialsKeys, logger, configurationHook), @@ -116,7 +116,7 @@ public RedisConsumerChannel( string? password = null, RedisConsumerChannelSetting? setting = null, Action? configurationHook = null) : this( - new EventSourceRedisConnectionFacroty( + EventSourceRedisConnectionFactory.Create( logger, endpoint, password, diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs index ba9d1e25..789a3158 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -11,13 +11,13 @@ namespace EventSourcing.Backbone.Channels /// internal class RedisHashStorageStrategy : IConsumerStorageStrategy { - private readonly IEventSourceRedisConnectionFacroty _connFactory; + private readonly IEventSourceRedisConnectionFactory _connFactory; /// /// Initializes a new instance. /// /// The database task. - public RedisHashStorageStrategy(IEventSourceRedisConnectionFacroty connFactory) + public RedisHashStorageStrategy(IEventSourceRedisConnectionFactory connFactory) { _connFactory = connFactory; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs index 21567a1f..e9046b8c 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerBuilder.cs @@ -109,7 +109,7 @@ public static IProducerStoreStrategyBuilder UseRedisChannel( IProducerChannelProvider LocalCreate(ILogger logger) { - var connFactory = new EventSourceRedisConnectionFacroty(logger, configuration); + var connFactory = EventSourceRedisConnectionFactory.Create(logger, configuration); var channel = new RedisProducerChannel( connFactory, logger ?? EventSourceFallbakLogger.Default, @@ -129,7 +129,7 @@ IProducerChannelProvider LocalCreate(ILogger logger) /// internal static IProducerStoreStrategyBuilder UseRedisChannel( this IProducerBuilder builder, - IEventSourceRedisConnectionFacroty redisConnectionFactory, + IEventSourceRedisConnectionFactory redisConnectionFactory, AsyncPolicy? resiliencePolicy = null) { var result = builder.UseChannel(LocalCreate); @@ -152,15 +152,25 @@ IProducerChannelProvider LocalCreate(ILogger logger) /// The service provider. /// The resilience policy. /// - public static IProducerStoreStrategyBuilder GetRedisChannelService( + public static IProducerIocStoreStrategyBuilder ResolveRedisProducerChannel( this IProducerBuilder builder, IServiceProvider serviceProvider, AsyncPolicy? resiliencePolicy = null) { - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - return builder.UseRedisChannel(connFactory, resiliencePolicy); + var result = builder.UseChannel(serviceProvider, LocalCreate); + return result; + + IProducerChannelProvider LocalCreate(ILogger logger) + { + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFactory)} is not registered, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + var channel = new RedisProducerChannel( + connFactory, + logger ?? EventSourceFallbakLogger.Default, + resiliencePolicy); + return channel; + } } /// @@ -169,15 +179,12 @@ public static IProducerStoreStrategyBuilder GetRedisChannelService( /// The service provider. /// The resilience policy. /// - public static IProducerStoreStrategyBuilder GetRedisChannelService( + public static IProducerIocStoreStrategyBuilder ResolveRedisProducerChannel( this IServiceProvider serviceProvider, AsyncPolicy? resiliencePolicy = null) { - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFacroty)} is not registerd, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - var builder = ProducerBuilder.Empty; - return builder.UseRedisChannel(connFactory, resiliencePolicy); + var result = ProducerBuilder.Empty.ResolveRedisProducerChannel(serviceProvider, resiliencePolicy); + return result; } } } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 0a0d6b06..0087cbed 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -19,7 +19,7 @@ internal class RedisProducerChannel : IProducerChannelProvider private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); private readonly ILogger _logger; private readonly AsyncPolicy _resiliencePolicy; - private readonly IEventSourceRedisConnectionFacroty _connFactory; + private readonly IEventSourceRedisConnectionFactory _connFactory; private readonly IProducerStorageStrategy _defaultStorageStrategy; private const string META_SLOT = "____"; @@ -32,7 +32,7 @@ internal class RedisProducerChannel : IProducerChannelProvider /// The logger. /// The resilience policy for retry. public RedisProducerChannel( - IEventSourceRedisConnectionFacroty redisFactory, + IEventSourceRedisConnectionFactory redisFactory, ILogger logger, AsyncPolicy? resiliencePolicy) { diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 8711dc68..e034d6e5 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -15,7 +15,7 @@ namespace EventSourcing.Backbone.Channels /// internal class RedisHashStorageStrategy : IProducerStorageStrategy { - private readonly IEventSourceRedisConnectionFacroty _connFactory; + private readonly IEventSourceRedisConnectionFactory _connFactory; private readonly ILogger _logger; #region Ctor @@ -26,7 +26,7 @@ internal class RedisHashStorageStrategy : IProducerStorageStrategy /// The connection factory. /// The logger. public RedisHashStorageStrategy( - IEventSourceRedisConnectionFacroty connFactory, + IEventSourceRedisConnectionFactory connFactory, ILogger logger) { _connFactory = connFactory; diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs deleted file mode 100644 index 667507c7..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFacroty.cs +++ /dev/null @@ -1,74 +0,0 @@ -using Microsoft.Extensions.Logging; - -using StackExchange.Redis; - - -namespace EventSourcing.Backbone -{ - /// - /// Event Source connection (for IoC) - /// Because IConnectionMultiplexer may be used by other component, - /// It's more clear to wrap the IConnectionMultiplexer for easier resove by IoC. - /// This factory is also responsible of the connection health. - /// It will return same connection as long as it healthy. - /// - public sealed class EventSourceRedisConnectionFacroty : RedisConnectionFacrotyBase - { - #region Ctor - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The logger. - public EventSourceRedisConnectionFacroty( - ILogger logger, - ConfigurationOptions? configuration = null) : - base(logger, configuration) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The credential. - /// The logger. - /// The configuration hook. - public EventSourceRedisConnectionFacroty( - IRedisCredentials credential, - ILogger logger, - Action? configurationHook = null) : - base(credential, logger, configurationHook) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The raw endpoint (not an environment variable). - /// The password (not an environment variable). - /// The configuration hook. - public EventSourceRedisConnectionFacroty( - ILogger logger, - string endpoint, - string? password = null, - Action? configurationHook = null) : - base(logger, endpoint, password, configurationHook) - { - } - - - - #endregion // Ctor - - #region Kind - - /// - /// Gets the kind. - /// - protected override string Kind { get; } = "Event-Sourcing"; - - #endregion // Kind - } -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs similarity index 70% rename from Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs index 9fcda858..598a3938 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisConnectionFacrotyBase.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs @@ -1,7 +1,11 @@ -using Microsoft.Extensions.Logging; +using System.Net; + +using Microsoft.Extensions.Logging; using StackExchange.Redis; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; + #pragma warning disable S3881 // "IDisposable" should be implemented correctly #pragma warning disable S2953 // Methods named "Dispose" should implement "IDisposable.Dispose" @@ -15,10 +19,9 @@ namespace EventSourcing.Backbone /// This factory is also responsible of the connection health. /// It will return same connection as long as it healthy. ///
- public abstract class RedisConnectionFacrotyBase : IEventSourceRedisConnectionFacroty, IDisposable, IAsyncDisposable + public class EventSourceRedisConnectionFactory : IEventSourceRedisConnectionFactory, IDisposable, IAsyncDisposable { private const int CLOSE_DELEY_MILLISECONDS = 5000; - private static readonly IRedisCredentials _redisCredentials = new RedisCredentialsEnvKeys(); private Task _redisTask; private readonly ILogger _logger; private readonly ConfigurationOptions _configuration; @@ -28,87 +31,117 @@ public abstract class RedisConnectionFacrotyBase : IEventSourceRedisConnectionFa #region Ctor - #region Overloads - /// /// Constructor /// /// The logger. + /// The configuration. + public EventSourceRedisConnectionFactory( + ILogger logger, + ConfigurationOptions? configuration = null) + : this ((ILogger)logger, configuration) + { + } + /// - /// Create REDIS configuration options. + /// Constructor /// - /// The configuration hook. - protected RedisConnectionFacrotyBase( + /// The logger. + /// The configuration. + private EventSourceRedisConnectionFactory( ILogger logger, - Action? configurationHook = null) - : this(new RedisCredentialsEnvKeys(), logger, configurationHook) + ConfigurationOptions? configuration = null) { + _logger = logger; + if (configuration == null) + { + var cred = new RedisCredentialsEnvKeys(); + _configuration = cred.CreateConfigurationOptions(); + } + else + { + _configuration = configuration; + } + _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); } + #endregion // Ctor + + #region Create + /// - /// Constructor + /// Create instance /// + /// The configuration. /// The logger. + /// + public static IEventSourceRedisConnectionFactory Create( + ILogger logger, + ConfigurationOptions? configuration = null) + { + return new EventSourceRedisConnectionFactory(logger, configuration); + } + /// - /// Create REDIS configuration options. + /// Create instance /// - /// The raw endpoint (not an environment variable). - /// The password (not an environment variable). + /// The credential. + /// The logger. /// The configuration hook. - protected RedisConnectionFacrotyBase( + /// + public static IEventSourceRedisConnectionFactory Create( + IRedisCredentials credential, ILogger logger, - string endpoint, - string? password = null, Action? configurationHook = null) - : this(new RedisCredentialsRaw(endpoint, password), logger, configurationHook) - { + var configuration = credential.CreateConfigurationOptions(); + return new EventSourceRedisConnectionFactory(logger, configuration); } - - #endregion // Overloads - /// - /// Constructor + /// Create instance /// - /// The credential. /// The logger. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). /// The configuration hook. - protected RedisConnectionFacrotyBase( - IRedisCredentials credential, + /// + public static IEventSourceRedisConnectionFactory Create( ILogger logger, + string endpoint, + string? password = null, Action? configurationHook = null) - { - _logger = logger; - _configuration = credential.CreateConfigurationOptions(configurationHook); - _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); + var credential = new RedisCredentialsRaw(endpoint, password); + return Create(credential, logger, configurationHook); } - /// - /// Constructor + /// Create instance from environment variable /// /// The logger. - /// The configuration. - protected RedisConnectionFacrotyBase( - ILogger logger, - ConfigurationOptions? configuration) + /// The endpoint. + /// The password. + /// The configuration hook. + /// + public static IEventSourceRedisConnectionFactory CreateFromEnv( + ILogger logger, + string endpointEnvKey, + string passwordEnvKey = PASSWORD_KEY, + Action? configurationHook = null) { - _logger = logger; - _configuration = configuration ?? _redisCredentials.CreateConfigurationOptions(); - _redisTask = RedisClientFactory.CreateProviderAsync(_configuration, logger); + var credential = new RedisCredentialsEnvKeys(endpointEnvKey, passwordEnvKey); + return Create(credential, logger, configurationHook); } - - #endregion // Ctor + #endregion // Create #region Kind /// /// Gets the kind. /// - protected abstract string Kind { get; } + protected virtual string Kind { get; } = "Event-Sourcing"; #endregion // Kind @@ -117,7 +150,7 @@ protected RedisConnectionFacrotyBase( /// /// Get a valid connection /// - async Task IEventSourceRedisConnectionFacroty.GetAsync() + async Task IEventSourceRedisConnectionFactory.GetAsync() { var conn = await _redisTask; if (conn.IsConnected) @@ -158,9 +191,9 @@ async Task IEventSourceRedisConnectionFacroty.GetAsync() /// /// Get database /// - async Task IEventSourceRedisConnectionFacroty.GetDatabaseAsync() + async Task IEventSourceRedisConnectionFactory.GetDatabaseAsync() { - IEventSourceRedisConnectionFacroty self = this; + IEventSourceRedisConnectionFactory self = this; IConnectionMultiplexer conn = await self.GetAsync(); IDatabaseAsync db = conn.GetDatabase(); return db; @@ -222,7 +255,7 @@ public async ValueTask DisposeAsync() /// /// Finalizer /// - ~RedisConnectionFacrotyBase() + ~EventSourceRedisConnectionFactory() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: false); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs similarity index 87% rename from Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs index 490d0ec3..ecb64382 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFacroty.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs @@ -5,7 +5,7 @@ namespace EventSourcing.Backbone /// /// Connection factory /// - public interface IEventSourceRedisConnectionFacroty + public interface IEventSourceRedisConnectionFactory { /// /// Get a valid connection diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs index 0416182a..41c898b3 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/RedisCredentialsEnvKeys.cs @@ -6,15 +6,5 @@ namespace EventSourcing.Backbone /// /// Environment keys for REDIS's credentials /// - public record RedisCredentialsEnvKeys : IRedisCredentials - { - /// - /// Environment key of the end-point, if missing it use a default ('REDIS_EVENT_SOURCE_ENDPOINT'). - /// - public string? Endpoint { get; init; } = END_POINT_KEY; - /// - /// Environment key of the password, if missing it use a default ('REDIS_EVENT_SOURCE_PASS'). - /// - public string? Password { get; init; } = PASSWORD_KEY; - } + public record RedisCredentialsEnvKeys(string Endpoint = END_POINT_KEY, string Password = PASSWORD_KEY) : IRedisCredentials; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 607b2e5a..72785ce7 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -25,7 +26,7 @@ public static class RedisCommonProviderExtensions /// The logger. /// public static async Task CreateConsumerGroupIfNotExistsAsync( - this IEventSourceRedisConnectionFacroty connFactory, + this IEventSourceRedisConnectionFactory connFactory, string eventSourceKey, string consumerGroup, ILogger logger) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs index 6d386c01..7669f5f3 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs @@ -1,6 +1,9 @@ using EventSourcing.Backbone; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; namespace Microsoft.Extensions.Configuration { @@ -17,7 +20,64 @@ public static class RedisDiExtensions public static IServiceCollection AddEventSourceRedisConnection( this IServiceCollection services) { - services.AddSingleton(); + services.AddSingleton(); + + return services; + } + + /// + /// Adds the event source redis connection to the DI. + /// + /// The services. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// + /// An IServiceCollection. + /// + public static IServiceCollection AddEventSourceRedisConnection( + this IServiceCollection services, + string endpoint, + string? password = null) + { + services.AddSingleton( + sp => + { + ILogger logger = sp.GetService>() ?? + throw new EventSourcingException( + $"{nameof(AddEventSourceRedisConnection)}: Cannot resolve a logger"); + + var factory = EventSourceRedisConnectionFactory.Create(logger, endpoint, password); + return factory; + }); + + return services; + } + + /// + /// Adds the event source redis connection to the DI. + /// + /// The services. + /// The environment variable key of the endpoint. + /// The environment variable key of the password. + /// + /// An IServiceCollection. + /// + public static IServiceCollection AddEventSourceRedisConnectionFromEnv( + this IServiceCollection services, + string endpointEnvKey, + string passwordEnvKey = PASSWORD_KEY) + { + services.AddSingleton( + sp => + { + ILogger logger = sp.GetService>() ?? + throw new EventSourcingException( + $"{nameof(AddEventSourceRedisConnectionFromEnv)}: Cannot resolve a logger"); + + var factory = EventSourceRedisConnectionFactory.CreateFromEnv(logger, endpointEnvKey, passwordEnvKey); + return factory; + }); + return services; } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj index 051b3d11..867c07d3 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/EventSourcing.Backbone.Channels.S3StoreConsumerProvider.csproj @@ -11,6 +11,10 @@ + + + + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 9863cf90..3bf8d79d 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -6,6 +6,7 @@ using EventSourcing.Backbone.Channels; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; namespace EventSourcing.Backbone @@ -51,13 +52,16 @@ ValueTask Local(ILogger logger) /// Adds the S3 storage strategy. /// /// The builder. - /// The credentials. + /// + /// S3 client. + /// Learn how to setup an AWS client: https://codewithmukesh.com/blog/aws-credentials-for-dotnet-applications/ + /// /// The options. /// Type of the target. /// public static IConsumerStoreStrategyBuilder AddS3Strategy( this IConsumerStoreStrategyBuilder builder, - AWSCredentials credentials, + IAmazonS3 client, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All) { @@ -65,7 +69,7 @@ public static IConsumerStoreStrategyBuilder AddS3Strategy( ValueTask Local(ILogger logger) { - var factory = S3RepositoryFactory.Create(logger, credentials); + var factory = S3RepositoryFactory.Create(logger, client); var repo = factory.Get(options); var strategy = new S3ConsumerStorageStrategy(repo); return strategy.ToValueTask(); @@ -77,56 +81,17 @@ ValueTask Local(ILogger logger) /// Adds the S3 storage strategy. ///
/// The builder. - /// The credentials. - /// The configuration. /// The options. /// Type of the target. /// - public static IConsumerStoreStrategyBuilder AddS3Strategy( - this IConsumerStoreStrategyBuilder builder, - AWSCredentials credentials, - AmazonS3Config configuration, + public static IConsumerStoreStrategyBuilder ResolveS3Strategy( + this IConsumerIocStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All) { - var result = builder.AddStorageStrategyFactory(Local, targetType); - - ValueTask Local(ILogger logger) - { - var factory = S3RepositoryFactory.Create(logger, credentials, configuration); - var repo = factory.Get(options); - var strategy = new S3ConsumerStorageStrategy(repo); - return strategy.ToValueTask(); - } + IAmazonS3 s3Client = builder.ServiceProvider.GetService() ?? throw new EventSourcingException("IAmazonS3 is not registered"); + var result = builder.AddS3Strategy(s3Client, options, targetType); return result; } - - /// - /// Adds the S3 storage strategy. - /// - /// The builder. - /// The configuration. - /// The options. - /// Type of the target. - /// - public static IConsumerStoreStrategyBuilder AddS3Strategy( - this IConsumerStoreStrategyBuilder builder, - AmazonS3Config configuration, - S3Options options = default, - EventBucketCategories targetType = EventBucketCategories.All) - { - var result = builder.AddStorageStrategyFactory(Local, targetType); - - ValueTask Local(ILogger logger) - { - var factory = S3RepositoryFactory.Create(logger, configuration); - var repo = factory.Get(options); - var strategy = new S3ConsumerStorageStrategy(repo); - return strategy.ToValueTask(); - } - return result; - } - - } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj index d71efa88..a28a0266 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/EventSourcing.Backbone.Channels.S3StoreProducerProvider.csproj @@ -11,6 +11,10 @@ + + + + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index 7ecdc776..a53731e5 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; namespace EventSourcing.Backbone { @@ -53,16 +54,17 @@ ValueTask Local(ILogger logger) /// Adds the S3 storage strategy. ///
/// The builder. - /// The credentials. - /// The configuration. + /// + /// S3 client. + /// Learn how to setup an AWS client: https://codewithmukesh.com/blog/aws-credentials-for-dotnet-applications/ + /// /// The options. /// Type of the target. /// The filter of which keys in the bucket will be store into this storage. /// public static IProducerStoreStrategyBuilder AddS3Strategy( this IProducerStoreStrategyBuilder builder, - AWSCredentials credentials, - AmazonS3Config configuration, + IAmazonS3 client, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All, Predicate? filter = null) @@ -71,7 +73,7 @@ public static IProducerStoreStrategyBuilder AddS3Strategy( ValueTask Local(ILogger logger) { - var factory = S3RepositoryFactory.Create(logger, credentials, configuration); + var factory = S3RepositoryFactory.Create(logger, client); var repo = factory.Get(options); var strategy = new S3ProducerStorageStrategy(repo); return strategy.ToValueTask(); @@ -84,57 +86,18 @@ ValueTask Local(ILogger logger) /// Adds the S3 storage strategy. ///
/// The builder. - /// The configuration. /// The options. /// Type of the target. /// The filter of which keys in the bucket will be store into this storage. /// - public static IProducerStoreStrategyBuilder AddS3Strategy( - this IProducerStoreStrategyBuilder builder, - AmazonS3Config configuration, + public static IProducerStoreStrategyBuilder Resolve3Strategy( + this IProducerIocStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All, Predicate? filter = null) { - var result = builder.AddStorageStrategy(Local, targetType, filter); - - ValueTask Local(ILogger logger) - { - var factory = S3RepositoryFactory.Create(logger, configuration); - var repo = factory.Get(options); - var strategy = new S3ProducerStorageStrategy(repo); - return strategy.ToValueTask(); - } - - return result; - } - - /// - /// Adds the S3 storage strategy. - /// - /// The builder. - /// The credentials. - /// The options. - /// Type of the target. - /// The filter of which keys in the bucket will be store into this storage. - /// - public static IProducerStoreStrategyBuilder AddS3Strategy( - this IProducerStoreStrategyBuilder builder, - AWSCredentials credentials, - S3Options options = default, - EventBucketCategories targetType = EventBucketCategories.All, - Predicate? filter = null) - { - var result = builder.AddStorageStrategy(Local, targetType, filter); - - ValueTask Local(ILogger logger) - { - var factory = S3RepositoryFactory.Create(logger, credentials); - var repo = factory.Get(options); - var strategy = new S3ProducerStorageStrategy(repo); - return strategy.ToValueTask(); - } - + IAmazonS3 s3Client = builder.ServiceProvider.GetService() ?? throw new EventSourcingException("IAmazonS3 is not registered"); + IProducerStoreStrategyBuilder result = builder.AddS3Strategy(s3Client, options, targetType, filter); return result; } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index 0104386e..7bbc5ac3 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -28,7 +28,6 @@ public sealed class S3Repository : IS3Repository, IDisposable private static readonly List EMPTY_TAGS = new List(); private int _disposeCount = 0; private readonly S3EnvironmentConvention _environmentConvension; - private const StringComparison STRING_COMPARISON = StringComparison.OrdinalIgnoreCase; private readonly bool _dryRun; #region Ctor @@ -36,7 +35,10 @@ public sealed class S3Repository : IS3Repository, IDisposable /// /// Initializes a new instance. /// - /// S3 client. + /// + /// S3 client. + /// Learn how to setup an AWS client: https://codewithmukesh.com/blog/aws-credentials-for-dotnet-applications/ + /// /// The logger. /// The s3 options. public S3Repository( diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index a99b6986..7d3c9981 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -59,40 +59,14 @@ public static IS3RepositoryFactory Create(ILogger logger, /// Creates the specified logger. ///
/// The logger. - /// The credentials. + /// + /// S3 client. + /// Learn how to setup an AWS client: https://codewithmukesh.com/blog/aws-credentials-for-dotnet-applications/ + /// /// public static IS3RepositoryFactory Create(ILogger logger, - AWSCredentials credentials) + IAmazonS3 client) { - var client = new AmazonS3Client(credentials); - return new S3RepositoryFactory(logger, client); - } - - /// - /// Creates the specified logger. - /// - /// The logger. - /// The credentials. - /// The configuration. - /// - public static IS3RepositoryFactory Create(ILogger logger, - AWSCredentials credentials, - AmazonS3Config configuration) - { - var client = new AmazonS3Client(credentials, configuration); - return new S3RepositoryFactory(logger, client); - } - - /// - /// Creates the specified logger. - /// - /// The logger. - /// The configuration. - /// - public static IS3RepositoryFactory Create(ILogger logger, - AmazonS3Config configuration) - { - var client = new AmazonS3Client(configuration); return new S3RepositoryFactory(logger, client); } @@ -107,7 +81,7 @@ public static IS3RepositoryFactory Create(ILogger logger, /// The client. public S3RepositoryFactory( ILogger logger, - AmazonS3Client client) + IAmazonS3 client) { _logger = logger; diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index f45f48e4..daa157c5 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -20,7 +20,7 @@ namespace EventSourcing.Backbone public partial class ConsumerBuilder : IConsumerBuilder, IConsumerReadyBuilder, - IConsumerStoreStrategyBuilder + IConsumerIocStoreStrategyBuilder { private readonly ConsumerPlan _plan = ConsumerPlan.Empty; @@ -65,6 +65,7 @@ private ConsumerBuilder(ConsumerPlan plan) /// Choose the communication channel provider. ///
/// The channel provider. + /// Dependency injection provider. /// IConsumerStoreStrategyBuilder IConsumerBuilder.UseChannel( Func channel) @@ -74,6 +75,21 @@ IConsumerStoreStrategyBuilder IConsumerBuilder.UseChannel( return result; } + /// + /// Choose the communication channel provider. + /// + /// Dependency injection provider. + /// The channel provider. + /// + IConsumerIocStoreStrategyBuilder IConsumerBuilder.UseChannel( + IServiceProvider serviceProvider, + Func channel) + { + var prms = _plan.WithChannelFactory(channel, serviceProvider); + var result = new ConsumerBuilder(prms); + return result; + } + #endregion // UseChannel #region AddStorageStrategy @@ -177,6 +193,16 @@ IConsumerSubscribeBuilder IConsumerEnvironmentOfBuilder + /// Gets the service provider. + ///
+ /// ServiceProvider is null + IServiceProvider IConsumerIocStoreStrategyBuilder.ServiceProvider => _plan.ServiceProvider ?? throw new EventSourcingException("ServiceProvider is null"); + + #endregion // ServiceProvider + #region Uri /// diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index 55479f7a..f8fa5da1 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -71,7 +71,8 @@ private ConsumerPlan( string? consumerGroup = null, string? consumerName = null, AsyncPolicy? resiliencePolicy = null, - Func>? storageStrategyFactories = null) + Func>? storageStrategyFactories = null, + IServiceProvider? serviceProvider = null) { ChannelFactory = channelFactory ?? copyFrom.ChannelFactory; _channel = channel ?? copyFrom._channel; @@ -85,6 +86,7 @@ private ConsumerPlan( ConsumerGroup = consumerGroup ?? copyFrom.ConsumerGroup; ConsumerName = consumerName ?? copyFrom.ConsumerName; ResiliencePolicy = resiliencePolicy ?? copyFrom.ResiliencePolicy; + ServiceProvider = serviceProvider ?? copyFrom.ServiceProvider; StorageStrategyFactories = storageStrategyFactories == null ? copyFrom.StorageStrategyFactories : copyFrom.StorageStrategyFactories.Add(storageStrategyFactories); @@ -267,6 +269,15 @@ IConsumerChannelProvider IConsumerPlan.Channel #endregion // ResiliencePolicy + #region ServiceProvider + + /// + /// Gets the service provider. + /// + public IServiceProvider? ServiceProvider { get; } + + #endregion // ServiceProvider + //------------------------------------------ #region GetParameterAsync @@ -300,10 +311,11 @@ async ValueTask IConsumerPlan.GetParameterAsync( /// Attach the channel. /// /// The channel. + /// Dependency injection provider. /// - internal ConsumerPlan WithChannelFactory(Func channel) + internal ConsumerPlan WithChannelFactory(Func channel, IServiceProvider? serviceProvider = null) { - return new ConsumerPlan(this, channelFactory: channel); + return new ConsumerPlan(this, channelFactory: channel, serviceProvider: serviceProvider); } #endregion // WithChannel diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerIocStoreStrategyBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerIocStoreStrategyBuilder.cs new file mode 100644 index 00000000..a51462f9 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerIocStoreStrategyBuilder.cs @@ -0,0 +1,14 @@ +namespace EventSourcing.Backbone.Building +{ + /// + /// storage configuration with IoC. + /// + /// + public interface IConsumerIocStoreStrategyBuilder : IConsumerStoreStrategyBuilder + { + /// + /// Gets the service provider. + /// + IServiceProvider ServiceProvider { get; } + } +} diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStoreStrategyBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStoreStrategyBuilder.cs index c91b5fb3..b67579b9 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStoreStrategyBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerStoreStrategyBuilder.cs @@ -5,7 +5,7 @@ namespace EventSourcing.Backbone.Building { /// - /// Enable configuration. + /// storage configuration. /// public interface IConsumerStoreStrategyBuilder : IConsumerOptionsBuilder { diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs index 43cc9621..265e86ae 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs @@ -1,4 +1,6 @@ -using EventSourcing.Backbone.Building; +using System; + +using EventSourcing.Backbone.Building; using Microsoft.Extensions.Logging; @@ -15,5 +17,13 @@ public interface IConsumerBuilder /// The channel provider. /// IConsumerStoreStrategyBuilder UseChannel(Func channel); + + /// + /// Choose the communication channel provider. + /// + /// Dependency injection provider. + /// The channel provider. + /// + IConsumerIocStoreStrategyBuilder UseChannel(IServiceProvider serviceProvider, Func channel); } } diff --git a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerIocStoreStrategyBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerIocStoreStrategyBuilder.cs new file mode 100644 index 00000000..2c1d9528 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerIocStoreStrategyBuilder.cs @@ -0,0 +1,14 @@ +namespace EventSourcing.Backbone +{ + /// + /// storage configuration with IoC. + /// + /// + public interface IProducerIocStoreStrategyBuilder : IProducerStoreStrategyBuilder + { + /// + /// Gets the service provider. + /// + IServiceProvider ServiceProvider { get; } + } +} diff --git a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStoreStrategyBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStoreStrategyBuilder.cs index d6bf644b..ee72dcfb 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStoreStrategyBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerStoreStrategyBuilder.cs @@ -6,7 +6,7 @@ namespace EventSourcing.Backbone { /// - /// Enable configuration. + /// storage configuration. /// /// public interface IProducerStoreStrategyBuilder : IProducerOptionsBuilder diff --git a/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs index 74423cbd..d7d1ab1e 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs @@ -1,4 +1,6 @@ -using EventSourcing.Backbone.Building; +using System; + +using EventSourcing.Backbone.Building; using Microsoft.Extensions.Logging; @@ -17,6 +19,16 @@ public interface IProducerBuilder IProducerStoreStrategyBuilder UseChannel( Func channel); + /// + /// Choose the communication channel provider. + /// + /// The channel provider. + /// Dependency injection provider. + /// + IProducerIocStoreStrategyBuilder UseChannel( + IServiceProvider serviceProvider, + Func channel); + /// /// Merges multiple channels of same contract into single /// producer for broadcasting messages via all channels. diff --git a/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs index dcd797d1..9a60eacd 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs @@ -55,7 +55,8 @@ private ProducerPlan( IImmutableList? routes = null, IImmutableList? forwards = null, IImmutableList? forwardPlans = null, - Func>? storageStrategyFactories = null) + Func>? storageStrategyFactories = null, + IServiceProvider? serviceProvider = null) { ChannelFactory = channelFactory ?? copyFrom.ChannelFactory; _channel = channel ?? copyFrom._channel; @@ -67,6 +68,7 @@ private ProducerPlan( Routes = routes ?? copyFrom.Routes; Forwards = forwards ?? copyFrom.Forwards; _forwardPlans = forwardPlans ?? copyFrom._forwardPlans; + ServiceProvider = serviceProvider ?? copyFrom.ServiceProvider; Logger = logger ?? copyFrom.Logger; StorageStrategyFactories = storageStrategyFactories == null ? copyFrom.StorageStrategyFactories @@ -207,6 +209,15 @@ IProducerChannelProvider IProducerPlan.Channel #endregion // Forwards + #region ServiceProvider + + /// + /// Gets the service provider. + /// + public IServiceProvider? ServiceProvider { get; } + + #endregion // ServiceProvider + #region ForwardChannels private readonly IImmutableList _forwardPlans = ImmutableList.Empty; @@ -237,10 +248,11 @@ IProducerChannelProvider IProducerPlan.Channel /// Assign channel. /// /// The channel factory. + /// Dependency injection provider. /// - public ProducerPlan UseChannel(Func channelFactory) + public ProducerPlan UseChannel(Func channelFactory, IServiceProvider? serviceProvider = null) { - return new ProducerPlan(this, channelFactory: channelFactory); + return new ProducerPlan(this, channelFactory: channelFactory, serviceProvider: serviceProvider); } #endregion // WithOptions diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index 54445f43..d70d6a20 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -12,7 +12,7 @@ namespace EventSourcing.Backbone public class ProducerBuilder : IProducerBuilder, IProducerHooksBuilder, - IProducerStoreStrategyBuilder + IProducerIocStoreStrategyBuilder { /// @@ -98,8 +98,32 @@ IProducerStoreStrategyBuilder IProducerBuilder.UseChannel( return new ProducerBuilder(prms); } + /// + /// Choose the communication channel provider. + /// + /// The channel provider. + /// Dependency injection provider. + /// + IProducerIocStoreStrategyBuilder IProducerBuilder.UseChannel( + IServiceProvider serviceProvider, + Func channel) + { + var prms = Plan.UseChannel(channel, serviceProvider); + return new ProducerBuilder(prms); + } + #endregion // UseChannel + #region ServiceProvider + + /// + /// Gets the service provider. + /// + IServiceProvider IProducerIocStoreStrategyBuilder.ServiceProvider => + Plan?.ServiceProvider ?? throw new EventSourcingException("ServiceProvider is null"); + + #endregion // ServiceProvider + #region AddStorageStrategy /// diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs new file mode 100644 index 00000000..1f091c94 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs @@ -0,0 +1,13 @@ +using System.ComponentModel; +using System.Text.Json; + +namespace EventSourcing.Backbone.WebEventTest +{ + [EventsContract(EventsContractType.Consumer)] + [EventsContract(EventsContractType.Producer)] + [Obsolete("Used for code generation, use the producer / consumer version of it", true)] + public interface IS3Test + { + ValueTask NameAsync(string payload); + } +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs index a58bd197..0c3f6d16 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs @@ -9,11 +9,11 @@ namespace EventSourcing.Backbone.WebEventTest.Controllers public class TestController : ControllerBase { private readonly ILogger _logger; - private readonly IEventSourceRedisConnectionFacroty _connFacroty; + private readonly IEventSourceRedisConnectionFactory _connFacroty; public TestController( ILogger logger, - IEventSourceRedisConnectionFacroty connFacroty) + IEventSourceRedisConnectionFactory connFacroty) { _logger = logger; _connFacroty = connFacroty; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index 5c863870..53d8527a 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -21,6 +21,7 @@ + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index b5873d71..491ec189 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -1,5 +1,7 @@ using System.Text.Json; +using Amazon.S3; + using EventSourcing.Backbone.WebEventTest; using EventSourcing.Backbone.WebEventTest.Jobs; @@ -13,11 +15,13 @@ var builder = WebApplication.CreateBuilder(args); +builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); +builder.Services.AddAWSService(); + IWebHostEnvironment environment = builder.Environment; string env = environment.EnvironmentName; string appName = environment.ApplicationName; -string shortAppName = appName.Replace("", string.Empty) - .Replace("Backend.", string.Empty); +string shortAppName = appName.Replace("EventSourcing.Backbone.", string.Empty); var services = builder.Services; @@ -29,16 +33,11 @@ services.AddSwaggerGen( opt => { - //opt.UseInlineDefinitionsForEnums(); - //opt.UseOneOfForPolymorphism(); - //opt.UseAllOfToExtendReferenceSchemas(); - //opt.UseAllOfForInheritance(); opt.SupportNonNullableReferenceTypes(); opt.IgnoreObsoleteProperties(); opt.IgnoreObsoleteActions(); opt.DescribeAllParametersInCamelCase(); - opt.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", @@ -47,24 +46,7 @@

docker run -p 6379:6379 -it --rm --name redis-Json redislabs/rejson:latest

docker run --rm -it --name jaeger -p 13133:13133 -p 16686:16686 -p 4317:55680 jaegertracing/opentelemetry-all-in-one

", - - //TermsOfService = new Uri("https://example.com/terms"), - //Contact = new OpenApiContact - //{ - // Name = "Example Contact", - // Url = new Uri("https://example.com/contact") - //}, - //License = new OpenApiLicense - //{ - // Name = "Example License", - // Url = new Uri("https://example.com/license") - //} }); - - //opt.SwaggerDoc("Event Source", new Microsoft.OpenApi.Models.OpenApiInfo { Description = "Bla" }); - //opt.SelectSubTypesUsing(); - //opt.SelectDiscriminatorValueUsing(); - //opt.SelectDiscriminatorNameUsing(); }); services.AddHostedService(); services.AddHostedService(); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index f5cc7967..7611ec59 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -18,19 +18,20 @@ public static IServiceCollection AddEventSource( this IServiceCollection services, Env env) { + var s3Options = new S3Options { Bucket = "event-sourcing-web" }; services.AddSingleton(ioc => { ILogger logger = ioc.GetService>() ?? throw new ArgumentNullException(); - IRawProducer producer = ProducerBuilder.Empty.GetRedisChannelService(ioc) - // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) + IRawProducer producer = ioc.ResolveRedisProducerChannel() + .Resolve3Strategy(s3Options) .BuildRaw(); return producer; }); services.AddSingleton(ioc => { ILogger logger = ioc.GetService>() ?? throw new ArgumentNullException(); - IEventFlowProducer producer = ProducerBuilder.Empty.GetRedisChannelService(ioc) - // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) + IEventFlowProducer producer = ioc.ResolveRedisProducerChannel() + .Resolve3Strategy(s3Options) .Environment(env) .Uri(URI) .WithLogger(logger) @@ -40,7 +41,8 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { IConsumerReadyBuilder consumer = - ioc.UseRedisChannelInjection() + ioc.ResolveRedisConsumerChannel() + .ResolveS3Strategy(s3Options) // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { @@ -54,7 +56,8 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { IConsumerHooksBuilder consumer = - ioc.UseRedisChannelInjection() + ioc.ResolveRedisConsumerChannel() + .ResolveS3Strategy(s3Options) // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { diff --git a/Tests/EventSourcing.Backbone.WebEventTest/appsettings.json b/Tests/EventSourcing.Backbone.WebEventTest/appsettings.json index d9d9a9bf..b689c6e4 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/appsettings.json +++ b/Tests/EventSourcing.Backbone.WebEventTest/appsettings.json @@ -6,5 +6,9 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "AWS": { + "Region": "us-east-1", + "Profile": "playground" + } } diff --git a/Tests/HelloWorld/ProducingEvents/Program.cs b/Tests/HelloWorld/ProducingEvents/Program.cs index 7d37c076..10adcb00 100644 --- a/Tests/HelloWorld/ProducingEvents/Program.cs +++ b/Tests/HelloWorld/ProducingEvents/Program.cs @@ -3,6 +3,7 @@ using EventSourcing.Backbone; using System; +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. Console.WriteLine("Producing events"); From f4cc927f9838812fb4f675575da909709e4e2362 Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 1 Jun 2023 09:57:10 +0000 Subject: [PATCH 087/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f9190088..a9aebeb9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.76 + 1.2.77 From 85e4c9f1be05a5f5f5dba5259d6c579317c6aa50 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Thu, 1 Jun 2023 09:57:27 +0000 Subject: [PATCH 088/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a9aebeb9..c8b446b6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.77 + 1.2.78 @@ -44,7 +44,7 @@ - + From 2da54209796ec905454064d75ed3bd1834294f70 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:25:02 +0300 Subject: [PATCH 089/178] Create FUNDING.yml --- .github/FUNDING.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..69ef038e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: [bnayae] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From c6ca2b1c568a0a670553459e8637f9ea37aaa7e1 Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 1 Jun 2023 10:25:17 +0000 Subject: [PATCH 090/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index c8b446b6..23fe6d68 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.78 + 1.2.79 From bdefa7a4bb621b1f1f451eb8b19a0fbb10adb857 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Thu, 1 Jun 2023 10:25:37 +0000 Subject: [PATCH 091/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 23fe6d68..1ed3de6e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.79 + 1.2.80 @@ -44,7 +44,7 @@ - + From fdb51b797f117cccfb167b1c3c9a94b626f80e1a Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:25:53 +0300 Subject: [PATCH 092/178] rfc: cleanup --- .../S3ConsumerStorageStrategy.cs | 2 -- .../BlobResponse.cs | 14 +++++++- .../IS3RepositoryFactory.cs | 2 +- .../S3Repository.cs | 18 ++++++++--- .../S3RepositoryFactory.cs | 32 ++++++++++++++++--- .../Builder/ConsumerBuilder.cs | 1 - .../Builder/ConsumerPlan.cs | 1 + .../Producer/Plan/ProducerPlan.cs | 1 + 8 files changed, 56 insertions(+), 15 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs index 4a832bed..ba79b759 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs @@ -70,8 +70,6 @@ async ValueTask IConsumerStorageStrategy.LoadBucketAsync( Func getProperty, CancellationToken cancellation) { - string id = meta.MessageId; - var lookup = ImmutableDictionary.CreateRange(prevBucket); string json = getProperty($"{Constants.PROVIDER_ID}~{type}"); var keyPathPairs = JsonSerializer.Deserialize[]>( json, SerializerOptionsWithIndent) ?? diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs index c5c623f0..31b1a321 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/BlobResponse.cs @@ -3,14 +3,16 @@ /// /// Response structure /// - public class BlobResponse : IEquatable + public sealed class BlobResponse : IEquatable { #region Ctor /// /// Prevents a default instance of the class from being created. /// +#pragma warning disable S1133 // Deprecated code should be removed [Obsolete("Use other constructors (this one exists to enable de-serialization)", true)] +#pragma warning restore S1133 // Deprecated code should be removed private BlobResponse() { } /// @@ -44,7 +46,9 @@ public BlobResponse( public string Key { get => _key; +#pragma warning disable S1133 // Deprecated code should be removed [Obsolete("Exposed for the serializer", true)] +#pragma warning restore S1133 // Deprecated code should be removed set => _key = value; } @@ -59,7 +63,9 @@ public string Key public string? FileName { get => _fileName; +#pragma warning disable S1133 // Deprecated code should be removed [Obsolete("Exposed for the serializer", true)] +#pragma warning restore S1133 // Deprecated code should be removed set => _fileName = value; } @@ -74,7 +80,9 @@ public string? FileName public string ETag { get => _eTag; +#pragma warning disable S1133 // Deprecated code should be removed [Obsolete("Exposed for the serializer", true)] +#pragma warning restore S1133 // Deprecated code should be removed set => _eTag = value; } @@ -89,7 +97,9 @@ public string ETag public string ContentVersion { get => _contentVersion; +#pragma warning disable S1133 // Deprecated code should be removed [Obsolete("Exposed for the serializer", true)] +#pragma warning restore S1133 // Deprecated code should be removed set => _contentVersion = value; } @@ -131,6 +141,7 @@ public bool Equals(BlobResponse? other) /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// +#pragma warning disable S2328 // "GetHashCode" should not reference mutable fields public override int GetHashCode() { return HashCode.Combine( @@ -139,6 +150,7 @@ public override int GetHashCode() _eTag, _contentVersion); } +#pragma warning restore S2328 // "GetHashCode" should not reference mutable fields /// diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs index c899b1f9..7260ccbb 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/IS3RepositoryFactory.cs @@ -7,6 +7,6 @@ public interface IS3RepositoryFactory /// /// /// - S3Repository Get(S3Options options = default); + IS3Repository Get(S3Options options = default); } } \ No newline at end of file diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index 7bbc5ac3..a2282cc4 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -15,7 +15,7 @@ namespace EventSourcing.Backbone.Channels /// /// Abstract S3 operations /// - public sealed class S3Repository : IS3Repository, IDisposable + internal sealed class S3Repository : IS3Repository, IDisposable { private static readonly string BUCKET = Environment.GetEnvironmentVariable("S3_EVENT_SOURCE_BUCKET") @@ -316,8 +316,11 @@ public async ValueTask SaveAsync( string key = GetKey(env, id); try { - var date = DateTime.UtcNow; - //tags = tags.Add("month", date.ToString("yyyy-MM")); + +#pragma warning disable S125 // Sections of code should not be commented out + // var date = DateTime.UtcNow; + // tags = tags.Add("month", date.ToString("yyyy-MM")); +#pragma warning restore S125 // Sections of code should not be commented out var s3Request = new PutObjectRequest { @@ -327,6 +330,7 @@ public async ValueTask SaveAsync( ContentType = mediaType, TagSet = tags?.Select(m => new Tag { Key = m.Key, Value = m.Value })?.ToList() ?? EMPTY_TAGS, }; + // s3Request.Headers.ExpiresUtc = DateTime.Now.AddHours(2); // cache expiration if (metadata != null) @@ -362,6 +366,8 @@ public async ValueTask SaveAsync( } #region Exception Handling +#pragma warning disable S2486 // Generic exceptions should not be ignored +#pragma warning disable S108 // Nested blocks of code should not be left empty catch (AmazonS3Exception e) { string json = ""; @@ -373,7 +379,7 @@ public async ValueTask SaveAsync( _logger.LogError(e.FormatLazy(), "AWS-S3 Failed to write: {payload}, {env}, {id}, {bucket}, {key}", json, env, id, bucket, key); string msg = $"AWS-S3 Failed to write: {env}, {id}, {bucket}, {key}"; - throw new ApplicationException(msg, e); + throw new EventSourcingException(msg, e); } catch (Exception e) { @@ -386,8 +392,10 @@ public async ValueTask SaveAsync( _logger.LogError(e.FormatLazy(), "S3 writing Failed: {payload}, {env}, {id}, {bucket}, {key}", json, env, id, bucket, key); string msg = $"S3 writing Failed: {env}, {id}, {bucket}, {key}"; - throw new ApplicationException(msg, e); + throw new EventSourcingException(msg, e); } +#pragma warning restore S2486 +#pragma warning restore S108 #endregion // Exception Handling } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index 7d3c9981..d3b3a061 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -18,18 +18,17 @@ public sealed class S3RepositoryFactory : IS3RepositoryFactory private readonly IAmazonS3 _client; private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); - #region Create + #region CreateClient /// - /// Creates the specified logger. + /// Creates the S3 client. /// - /// The logger. /// The access key or environment variable which hold it. /// The secret or environment variable which hold it . /// The region environment variable which hold it. /// if set to true [will try to find the value from environment variable. /// - public static IS3RepositoryFactory Create(ILogger logger, + public static IAmazonS3 CreateClient( string accessKey = "S3_EVENT_SOURCE_ACCESS_KEY", string secret = "S3_EVENT_SOURCE_SECRET", string region = "S3_EVENT_SOURCE_REGION", @@ -52,6 +51,29 @@ public static IS3RepositoryFactory Create(ILogger logger, ? RegionEndpoint.GetBySystemName(regionKey) : RegionEndpoint.USEast2; var client = new AmazonS3Client(accessKey, secret, rgnKey); + return client; + } + + #endregion // CreateClient + + #region Create + + /// + /// Creates the specified logger. + /// + /// The logger. + /// The access key or environment variable which hold it. + /// The secret or environment variable which hold it . + /// The region environment variable which hold it. + /// if set to true [will try to find the value from environment variable. + /// + public static IS3RepositoryFactory Create(ILogger logger, + string accessKey = "S3_EVENT_SOURCE_ACCESS_KEY", + string secret = "S3_EVENT_SOURCE_SECRET", + string region = "S3_EVENT_SOURCE_REGION", + bool fromEnvironment = true) + { + var client = CreateClient(accessKey, secret, region, fromEnvironment); return new S3RepositoryFactory(logger, client); } @@ -97,7 +119,7 @@ public S3RepositoryFactory( /// /// The options. /// - S3Repository IS3RepositoryFactory.Get(S3Options options) + IS3Repository IS3RepositoryFactory.Get(S3Options options) { var repo = _cache.GetOrAdd(options, CreateInternal); diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index daa157c5..10cbb7e0 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -65,7 +65,6 @@ private ConsumerBuilder(ConsumerPlan plan) /// Choose the communication channel provider. ///
/// The channel provider. - /// Dependency injection provider. /// IConsumerStoreStrategyBuilder IConsumerBuilder.UseChannel( Func channel) diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index f8fa5da1..35765745 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -56,6 +56,7 @@ private ConsumerPlan() /// Can use for observability. /// The resilience policy. /// The storage strategy. + /// The service provider. private ConsumerPlan( ConsumerPlan copyFrom, Func? channelFactory = null, diff --git a/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs index 9a60eacd..06bca15e 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs @@ -42,6 +42,7 @@ private ProducerPlan() /// Result of merging multiple channels. /// The forward channels. /// The storage strategy. + /// The service provider. private ProducerPlan( ProducerPlan copyFrom, Func? channelFactory = null, From 709b89c8f7a0e57fb240e82797bcd397dd836e18 Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 1 Jun 2023 12:26:17 +0000 Subject: [PATCH 093/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1ed3de6e..feaa9a7a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.80 + 1.2.81 From 8ea216d5cd24a1a0b24483c392e93dab2ff22fd4 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Thu, 1 Jun 2023 12:26:34 +0000 Subject: [PATCH 094/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index feaa9a7a..24de556f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.81 + 1.2.82 @@ -44,7 +44,7 @@ - + From 662b941914a7734def1fffd4b14162c3d288ea1d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Fri, 2 Jun 2023 11:15:21 +0300 Subject: [PATCH 095/178] rfc: s3 credentials and environment --- .../S3ConsumerStorageStrategyExtension.cs | 3 + .../S3ProducerStorageStrategyExtension.cs | 2 +- .../S3Options.cs | 2 +- .../S3Repository.cs | 2 + .../Builder/ConsumerBuilder.Iterator.cs | 18 +++ .../Builder/ConsumerBuilder.Receiver.cs | 20 +++ .../Builder/ConsumerBuilder.cs | 34 ++++ .../Route/IConsumerEnvironmentOfBuilder.cs | 7 + .../Building/IProducerBuilderEnvironment.cs | 14 +- .../Building/IProducerOptionsBuilder.cs | 2 +- .../Builder/ProducerBuilder.cs | 41 +++-- .../Contracts/ISimple.cs | 9 ++ ...tSourcing.Backbone.IntegrationTests.csproj | 130 ++++++++------- .../S3StoreCredentialsTests.cs | 148 ++++++++++++++++++ .../appsettings.json | 14 ++ .../xunit.runner.json | 4 + .../Program.cs | 26 --- .../RedisReconnectRetryPolicy.cs | 20 +++ .../RegisterEventSourceExtensions.cs | 7 +- .../RoslynHelper.cs | 4 +- 20 files changed, 402 insertions(+), 105 deletions(-) create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISimple.cs create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/appsettings.json create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 3bf8d79d..1960cc77 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -79,6 +79,9 @@ ValueTask Local(ILogger logger) /// /// Adds the S3 storage strategy. + /// Will resolve IAmazonS3 + /// See the following article for more details on how can you register Amazon credentials: + /// https://codewithmukesh.com/blog/aws-credentials-for-dotnet-applications/ /// /// The builder. /// The options. diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index a53731e5..2cd9d796 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -90,7 +90,7 @@ ValueTask Local(ILogger logger) /// Type of the target. /// The filter of which keys in the bucket will be store into this storage. /// - public static IProducerStoreStrategyBuilder Resolve3Strategy( + public static IProducerStoreStrategyBuilder ResolveS3Strategy( this IProducerIocStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All, diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs index f4a91a11..a106a8b9 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs @@ -11,7 +11,7 @@ public S3Options() { Bucket = null; BasePath = null; - EnvironmentConvension = S3EnvironmentConvention.None; + EnvironmentConvension = S3EnvironmentConvention.BucketPrefix; } /// diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index a2282cc4..a9d142bf 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -411,6 +411,8 @@ public async ValueTask SaveAsync( /// private string GetBucket(Env env) { + if (string.IsNullOrEmpty(env)) + return _bucket; var bucket = _environmentConvension switch { S3EnvironmentConvention.BucketPrefix => $"{env.Format()}.{_bucket}", diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs index a1bfd82a..8466e68a 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Iterator.cs @@ -49,6 +49,24 @@ IConsumerIterator IConsumerEnvironmentOfBuilder.Environment(E #endregion // Environment + #region Environment + + /// + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + IConsumerIterator IConsumerEnvironmentOfBuilder.EnvironmentFromVariable(string environmentVariableKey) + { + string environment = Environment.GetEnvironmentVariable(environmentVariableKey) ?? throw new EventSourcingException($"EnvironmentFromVariable failed, [{environmentVariableKey}] not found!"); + + IConsumerPlan plan = _plan.ChangeEnvironment(environment); + var result = new Iterator(plan); + return result; + } + + #endregion // Environment + #region Uri /// diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs index f917cb78..8ba79c58 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.Receiver.cs @@ -31,6 +31,26 @@ public Receiver(IConsumerPlan plan) #region Environment + /// + /// Environments from variable. + /// + /// The environment variable key. + /// + /// EnvironmentFromVariable failed, [{environmentVariableKey}] not found! + IConsumerReceiver IConsumerEnvironmentOfBuilder.EnvironmentFromVariable(string environmentVariableKey) + { + string environment = Environment.GetEnvironmentVariable(environmentVariableKey) ?? throw new EventSourcingException($"EnvironmentFromVariable failed, [{environmentVariableKey}] not found!"); + + + IConsumerPlan plan = _plan.ChangeEnvironment(environment); + var result = new Receiver(plan); + return result; + } + + #endregion // Environment + + #region Environment + /// /// Include the environment as prefix of the stream key. /// for example: env:URI diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index 10cbb7e0..7071af2d 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -192,6 +192,40 @@ IConsumerSubscribeBuilder IConsumerEnvironmentOfBuilder + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + /// EnvironmentFromVariable failed, [{environmentVariableKey}] not found! + IConsumerUriBuilder IConsumerEnvironmentOfBuilder>.EnvironmentFromVariable(string environmentVariableKey) + { + string environment = Environment.GetEnvironmentVariable(environmentVariableKey) ?? throw new EventSourcingException($"EnvironmentFromVariable failed, [{environmentVariableKey}] not found!"); + + var prms = _plan.WithEnvironment(environment); + var result = new ConsumerBuilder(prms); + return result; + } + + /// + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + /// EnvironmentFromVariable failed, [{environmentVariableKey}] not found! + IConsumerSubscribeBuilder IConsumerEnvironmentOfBuilder.EnvironmentFromVariable(string environmentVariableKey) + { + string environment = Environment.GetEnvironmentVariable(environmentVariableKey) ?? throw new EventSourcingException($"EnvironmentFromVariable failed, [{environmentVariableKey}] not found!"); + + var prms = _plan.WithEnvironment(environment); + var result = new ConsumerBuilder(prms); + return result; + } + + #endregion // EnvironmentFromVariable + #region ServiceProvider /// diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs index 3768ed15..c0e707ae 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/Route/IConsumerEnvironmentOfBuilder.cs @@ -12,5 +12,12 @@ public interface IConsumerEnvironmentOfBuilder /// The environment (null: keep current environment, empty: reset the environment to nothing). /// T Environment(Env? environment); + + /// + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + T EnvironmentFromVariable(string environmentVariableKey = "ASPNETCORE_ENVIRONMENT"); } } diff --git a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerBuilderEnvironment.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerBuilderEnvironment.cs index deaebb2e..c5372299 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerBuilderEnvironment.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerBuilderEnvironment.cs @@ -3,13 +3,23 @@ /// /// Origin environment of the message /// - public interface IProducerBuilderEnvironment + /// + public interface IProducerBuilderEnvironment { /// /// Origin environment of the message /// - /// The environment (null: keep current environment, empty: reset the environment to nothing). + /// + /// The environment (null: keep current environment, + /// empty: reset the environment to nothing). /// T Environment(Env? environment); + + /// + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + T EnvironmentFromVariable(string environmentVariableKey = "ASPNETCORE_ENVIRONMENT"); } } diff --git a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerOptionsBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerOptionsBuilder.cs index c8849f28..a0e2190f 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerOptionsBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/Building/IProducerOptionsBuilder.cs @@ -4,7 +4,7 @@ /// Enable configuration. /// public interface IProducerOptionsBuilder - : IProducerEnvironmentBuilder, IProducerLoggerBuilder + : IProducerEnvironmentBuilder { /// /// Apply configuration. diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index d70d6a20..ba21c4fc 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -186,6 +186,37 @@ IProducerSpecializeBuilder IProducerBuilderEnvironment + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + /// EnvironmentFromVariable failed, [{environmentVariableKey}] not found! + IProducerUriBuilder IProducerBuilderEnvironment.EnvironmentFromVariable(string environmentVariableKey) + { + string environment = Environment.GetEnvironmentVariable(environmentVariableKey) ?? throw new EventSourcingException($"EnvironmentFromVariable failed, [{environmentVariableKey}] not found!"); + var prms = Plan.WithEnvironment(environment); + return new ProducerBuilder(prms); + } + + + /// + /// Fetch the origin environment of the message from an environment variable. + /// + /// The environment variable key. + /// + /// EnvironmentFromVariable failed, [{environmentVariableKey}] not found! + IProducerSpecializeBuilder IProducerBuilderEnvironment.EnvironmentFromVariable(string environmentVariableKey) + { + string environment = Environment.GetEnvironmentVariable(environmentVariableKey) ?? throw new EventSourcingException($"EnvironmentFromVariable failed, [{environmentVariableKey}] not found!"); + var prms = Plan.WithEnvironment(environment); + return new ProducerBuilder(prms); + } + + #endregion // EnvironmentFromVariable + #region Key /// @@ -311,16 +342,6 @@ private ProducerBuilder WithLogger(ILogger logger) return new ProducerBuilder(prms); } - /// - /// Attach logger. - /// - /// The logger. - /// - IProducerOptionsBuilder IProducerLoggerBuilder.WithLogger(ILogger logger) - { - return WithLogger(logger); - } - /// /// Attach logger. /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISimple.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISimple.cs new file mode 100644 index 00000000..24b4f972 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/Contracts/ISimple.cs @@ -0,0 +1,9 @@ +namespace EventSourcing.Backbone.UnitTests.Entities; + +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +public interface ISimple +{ + ValueTask Step1Async(int value); + ValueTask Step2Async(int value); +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index 6db0e9c1..642bc1bd 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -1,62 +1,76 @@  - - Debug;Release;Gen - false - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Debug;Release;Gen + false + + + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs new file mode 100644 index 00000000..88258445 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs @@ -0,0 +1,148 @@ +using Xunit.Abstractions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Amazon.S3; +using EventSourcing.Backbone.Building; +using Amazon.Runtime.Internal.Util; +using FakeItEasy; +using StackExchange.Redis; +using System.Threading; +using System; +using Xunit; +using EventSourcing.Backbone.UnitTests.Entities; + +namespace EventSourcing.Backbone.Tests +{ + public sealed class S3StoreCredentialsTests : IDisposable + { + private const string TEST_URI = "testing"; + private readonly ITestOutputHelper _outputHelper; + private readonly IHost _host; + private const int TIMEOUT = 1000 * 20; + private readonly ISimpleConsumer _subscriber = A.Fake(); + + #region Ctor + + public S3StoreCredentialsTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "test"); + _host = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((context, builder) => + { + builder.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + }) + //.AddEnvironmentVariables() + .ConfigureServices((context, services) => + { + var s3Options = new S3Options { Bucket = "event-sourcing-validation" }; + + services.AddDefaultAWSOptions(context.Configuration.GetAWSOptions()); + services.AddAWSService(); + //IHostEnvironment environment = context.HostingEnvironment; + services.AddEventSourceRedisConnection(); + + services.AddSingleton(ioc => + { + var logger = ioc.GetService>(); + IProducerHooksBuilder producer = ioc.ResolveRedisProducerChannel() + .ResolveS3Strategy(s3Options) + .WithLogger(logger!) + .Uri(TEST_URI); + + return producer; + }); + services.AddSingleton(ioc => + { + //var logger = ioc.GetService>(); + IConsumerReadyBuilder consumer = ioc.ResolveRedisConsumerChannel() + .ResolveS3Strategy(s3Options) + .Uri(TEST_URI); + + return consumer; + }); + }) + .Build(); + } + + #endregion // Ctor + + #region S3_Cred + + [Fact(Timeout = TIMEOUT)] + //[Fact] + public async Task S3_Cred() + { + var producerBuilder = _host.Services.GetService() ?? throw new EventSourcingException("Producer is null"); + + ISimpleProducer producer = producerBuilder + .EnvironmentFromVariable() + .BuildSimpleProducer(); + + var id1 = producer.Step1Async(1); + var id2 = producer.Step2Async(2); + + IConsumerReadyBuilder consumerBuilder = _host.Services.GetService() ?? throw new EventSourcingException("Producer is null"); + IConsumerLifetime consumer = consumerBuilder + .EnvironmentFromVariable() + .WithOptions(opt => opt with { MaxMessages = 2 }) + .SubscribeSimpleConsumer(_subscriber); + + await consumer.Completion; + + A.CallTo(() => _subscriber.Step1Async(1)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.Step2Async(2)) + .MustHaveHappenedOnceExactly(); + } + + #endregion // S3_Cred + + #region S3_Cred_No_Env_Test + + //[Fact(Timeout = TIMEOUT)] + [Fact] + public async Task S3_Cred_No_Env_Test() + { + var producerBuilder = _host.Services.GetService() ?? throw new EventSourcingException("Producer is null"); + + ISimpleProducer producer = producerBuilder + .BuildSimpleProducer(); + + var id1 = producer.Step1Async(1); + var id2 = producer.Step2Async(2); + + IConsumerReadyBuilder consumerBuilder = _host.Services.GetService() ?? throw new EventSourcingException("Producer is null"); + IConsumerLifetime consumer = consumerBuilder + .WithOptions(opt => opt with { MaxMessages = 2 }) + .SubscribeSimpleConsumer(_subscriber); + + await consumer.Completion; + + A.CallTo(() => _subscriber.Step1Async(1)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.Step2Async(2)) + .MustHaveHappenedOnceExactly(); + } + + #endregion // S3_Cred_No_Env_Test + + + #region Dispose Pattern + + public void Dispose() + { + _host.Dispose(); + GC.SuppressFinalize(this); + } + + ~S3StoreCredentialsTests() + { + Dispose(); + } + + #endregion // Dispose Pattern + } +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/appsettings.json b/Tests/EventSourcing.Backbone.IntegrationTests/appsettings.json new file mode 100644 index 00000000..b689c6e4 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/appsettings.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "AWS": { + "Region": "us-east-1", + "Profile": "playground" + } +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json b/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json new file mode 100644 index 00000000..13b949c6 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 491ec189..adcfa446 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -11,8 +11,6 @@ using StackExchange.Redis; -const string ENV = $"test"; - var builder = WebApplication.CreateBuilder(args); builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); @@ -101,30 +99,6 @@ void RedisConfigEnrichment(ConfigurationOptions configuration) } app.UseHttpsRedirection(); - -//app.UseAuthorization(); - app.MapControllers(); app.Run(); - - -/// -/// Redis reconnect retry policy -/// -/// -public class RedisReconnectRetryPolicy : IReconnectRetryPolicy -{ - /// - /// Shoulds the retry. - /// - /// The current retry count. - /// The time elapsed milliseconds since last retry. - /// - bool IReconnectRetryPolicy.ShouldRetry( - long currentRetryCount, - int timeElapsedMillisecondsSinceLastRetry) - { - return timeElapsedMillisecondsSinceLastRetry > 1000; - } -} \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs b/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs new file mode 100644 index 00000000..05d67930 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs @@ -0,0 +1,20 @@ +using StackExchange.Redis; +/// +/// Redis reconnect retry policy +/// +/// +public class RedisReconnectRetryPolicy : IReconnectRetryPolicy +{ + /// + /// Shoulds the retry. + /// + /// The current retry count. + /// The time elapsed milliseconds since last retry. + /// + bool IReconnectRetryPolicy.ShouldRetry( + long currentRetryCount, + int timeElapsedMillisecondsSinceLastRetry) + { + return timeElapsedMillisecondsSinceLastRetry > 1000; + } +} \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 7611ec59..96bc7224 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -21,17 +21,16 @@ public static IServiceCollection AddEventSource( var s3Options = new S3Options { Bucket = "event-sourcing-web" }; services.AddSingleton(ioc => { - ILogger logger = ioc.GetService>() ?? throw new ArgumentNullException(); IRawProducer producer = ioc.ResolveRedisProducerChannel() - .Resolve3Strategy(s3Options) + .ResolveS3Strategy(s3Options) .BuildRaw(); return producer; }); services.AddSingleton(ioc => { - ILogger logger = ioc.GetService>() ?? throw new ArgumentNullException(); + ILogger logger = ioc.GetService>() ?? throw new EventSourcingException("Logger is missing"); IEventFlowProducer producer = ioc.ResolveRedisProducerChannel() - .Resolve3Strategy(s3Options) + .ResolveS3Strategy(s3Options) .Environment(env) .Uri(URI) .WithLogger(logger) diff --git a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs index 9a1d8797..0412864d 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs @@ -87,9 +87,9 @@ public static IEnumerable GetUsing(this SyntaxNode syntaxNode) } } - if(syntaxNode == null) + if(syntaxNode.Parent == null) yield break; - + foreach (var u in GetUsing(syntaxNode.Parent)) { yield return u; From 411e5436133cc72ff04bfc83d79fcaee6adba2ce Mon Sep 17 00:00:00 2001 From: bnayae Date: Fri, 2 Jun 2023 08:15:46 +0000 Subject: [PATCH 096/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 24de556f..04fd554e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.82 + 1.2.83 From ec630c5b50a4fb0065bf7569a5c1c5398344cc07 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Fri, 2 Jun 2023 08:16:11 +0000 Subject: [PATCH 097/178] Increment Version --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 04fd554e..9d276951 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.83 + 1.2.84 @@ -44,7 +44,7 @@ - + From 060340eaba1ea97e93d1f3c77ac210a4011ed70e Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:43:30 +0300 Subject: [PATCH 098/178] rfc: S3Strategy -> S3Storage --- .../S3ConsumerStorageStrategyExtension.cs | 6 +- .../S3ProducerStorageStrategyExtension.cs | 6 +- ...one.Channels.S3StoreProvider.Common.csproj | 2 +- Directory.Build.props | 2 + EventSourcing.Backbone.Abstractions/Env.cs | 4 +- EventSourcing.Backbone.sln | 18 ++ ...tSourcing.Backbone.IntegrationTests.csproj | 2 +- .../InheritanceS3StoreStrategyTests.cs | 4 +- .../MigrationReceiverTest.cs | 2 +- .../S3StoreStrategyExplicitTests.cs | 4 +- .../S3StoreStrategyStressTests .cs | 4 +- .../S3StoreStrategyTests.cs | 4 +- .../EventSourcing.Backbone.UnitTests.csproj | 2 +- .../AspCoreExtensions.cs | 16 +- .../Dockerfile.develop | 8 +- .../Program.cs | 5 +- .../RegisterEventSourceExtensions.cs | 6 +- .../azds.yaml | 12 +- .../Chart.yaml | 2 +- .../templates/NOTES.txt | 8 +- .../templates/_helpers.tpl | 6 +- .../templates/deployment.yaml | 12 +- .../templates/ingress.yaml | 6 +- .../templates/secrets.yaml | 2 +- .../templates/service.yaml | 8 +- .../values.yaml | 6 +- .../Controllers/ConsumerController.cs | 37 ++++ .../Controllers/ProducerController.cs | 86 +++++++++ Tests/WebSampleS3/Entities/Constants.cs | 40 ++++ .../WebSampleS3/Entities/IShipmentTracking.cs | 22 +++ Tests/WebSampleS3/Entities/Product.cs | 3 + Tests/WebSampleS3/Entities/User.cs | 3 + .../Extensions/ConsumerExtensions.cs | 47 +++++ .../Extensions/OpenTelemetryExtensions.cs | 153 +++++++++++++++ .../ShipmentTrackingProducerExtensions.cs | 43 +++++ Tests/WebSampleS3/Jobs/ConsumerJob.cs | 180 ++++++++++++++++++ Tests/WebSampleS3/Program.cs | 54 ++++++ .../Properties/launchSettings.json | 41 ++++ Tests/WebSampleS3/WebSampleS3.csproj | 50 +++++ .../WebSampleS3/appsettings.Development.json | 8 + Tests/WebSampleS3/appsettings.json | 13 ++ Tests/WebSampleS3/icon.png | Bin 0 -> 58881 bytes .../Properties/launchSettings.json | 2 +- 43 files changed, 868 insertions(+), 71 deletions(-) create mode 100644 Tests/WebSampleS3/Controllers/ConsumerController.cs create mode 100644 Tests/WebSampleS3/Controllers/ProducerController.cs create mode 100644 Tests/WebSampleS3/Entities/Constants.cs create mode 100644 Tests/WebSampleS3/Entities/IShipmentTracking.cs create mode 100644 Tests/WebSampleS3/Entities/Product.cs create mode 100644 Tests/WebSampleS3/Entities/User.cs create mode 100644 Tests/WebSampleS3/Extensions/ConsumerExtensions.cs create mode 100644 Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs create mode 100644 Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs create mode 100644 Tests/WebSampleS3/Jobs/ConsumerJob.cs create mode 100644 Tests/WebSampleS3/Program.cs create mode 100644 Tests/WebSampleS3/Properties/launchSettings.json create mode 100644 Tests/WebSampleS3/WebSampleS3.csproj create mode 100644 Tests/WebSampleS3/appsettings.Development.json create mode 100644 Tests/WebSampleS3/appsettings.json create mode 100644 Tests/WebSampleS3/icon.png diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 1960cc77..3eefd54e 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -27,7 +27,7 @@ public static class S3ConsumerStorageStrategyExtension /// Either the region or environment variable hold it (depend on the fromEnvironment parameters) /// if set to truelooks for the access key, secret and region in the environment variables. /// - public static IConsumerStoreStrategyBuilder AddS3Strategy( + public static IConsumerStoreStrategyBuilder AddS3Storage( this IConsumerStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All, @@ -59,7 +59,7 @@ ValueTask Local(ILogger logger) /// The options. /// Type of the target. /// - public static IConsumerStoreStrategyBuilder AddS3Strategy( + public static IConsumerStoreStrategyBuilder AddS3Storage( this IConsumerStoreStrategyBuilder builder, IAmazonS3 client, S3Options options = default, @@ -93,7 +93,7 @@ public static IConsumerStoreStrategyBuilder ResolveS3Strategy( EventBucketCategories targetType = EventBucketCategories.All) { IAmazonS3 s3Client = builder.ServiceProvider.GetService() ?? throw new EventSourcingException("IAmazonS3 is not registered"); - var result = builder.AddS3Strategy(s3Client, options, targetType); + var result = builder.AddS3Storage(s3Client, options, targetType); return result; } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index 2cd9d796..a1071aa7 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -27,7 +27,7 @@ public static class S3ProducerStorageStrategyExtension /// Either the region or environment variable hold it (depend on the fromEnvironment parameters) /// if set to truelooks for the access key, secret and region in the environment variables. /// - public static IProducerStoreStrategyBuilder AddS3Strategy( + public static IProducerStoreStrategyBuilder AddS3Storage( this IProducerStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All, @@ -62,7 +62,7 @@ ValueTask Local(ILogger logger) /// Type of the target. /// The filter of which keys in the bucket will be store into this storage. /// - public static IProducerStoreStrategyBuilder AddS3Strategy( + public static IProducerStoreStrategyBuilder AddS3Storage( this IProducerStoreStrategyBuilder builder, IAmazonS3 client, S3Options options = default, @@ -97,7 +97,7 @@ public static IProducerStoreStrategyBuilder ResolveS3Strategy( Predicate? filter = null) { IAmazonS3 s3Client = builder.ServiceProvider.GetService() ?? throw new EventSourcingException("IAmazonS3 is not registered"); - IProducerStoreStrategyBuilder result = builder.AddS3Strategy(s3Client, options, targetType, filter); + IProducerStoreStrategyBuilder result = builder.AddS3Storage(s3Client, options, targetType, filter); return result; } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index 6f928f34..cf2fa251 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/Directory.Build.props b/Directory.Build.props index 24de556f..d31e5d78 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,6 +2,8 @@ 1.2.82 + 1.2.85: + Breaking changes: S3Strategy was renamed to S3Storage diff --git a/EventSourcing.Backbone.Abstractions/Env.cs b/EventSourcing.Backbone.Abstractions/Env.cs index c65180a3..7bc9fbbd 100644 --- a/EventSourcing.Backbone.Abstractions/Env.cs +++ b/EventSourcing.Backbone.Abstractions/Env.cs @@ -47,13 +47,13 @@ public Env(string value) #region Format /// - /// Formats the specified environment into WeKnow convention. + /// Formats the specified environment into convention. /// /// public string Format() => Env.Format(_value); /// - /// Formats the specified environment into WeKnow convention. + /// Formats the specified environment into convention. /// /// private static string Format(string e) diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index b2b17bcb..aee46ab0 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -64,6 +64,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsumingEvents", "Tests\He EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProducingEvents", "Tests\HelloWorld\ProducingEvents\ProducingEvents.csproj", "{B528D83E-1AB9-4461-BA5E-993FF882F85B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebS3Sample", "Tests\WebS3Sample\WebS3Sample.csproj", "{4B0F7B88-F478-4577-B35E-647AA539A7F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSampleS3", "Tests\WebSampleS3\WebSampleS3.csproj", "{7A749C43-60F4-4DDC-894C-5E407B08A3A9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -167,6 +171,18 @@ Global {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Release|Any CPU.ActiveCfg = Release|Any CPU {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Release|Any CPU.Build.0 = Release|Any CPU + {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Gen|Any CPU.Build.0 = Gen|Any CPU + {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Release|Any CPU.Build.0 = Release|Any CPU + {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Gen|Any CPU.Build.0 = Gen|Any CPU + {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -192,6 +208,8 @@ Global {81EBE4CC-2E70-4E66-89DC-DCBB591343AB} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {A78DF256-6896-4246-8CF5-8CCBB27760A5} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {B528D83E-1AB9-4461-BA5E-993FF882F85B} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} + {4B0F7B88-F478-4577-B35E-647AA539A7F4} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} + {7A749C43-60F4-4DDC-894C-5E407B08A3A9} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {103E4CA1-859C-4E6A-9F2D-7FD54A8E687C} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index 642bc1bd..4595bc06 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -35,7 +35,7 @@ - + all diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs index c8be632f..fa6c4708 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceS3StoreStrategyTests.cs @@ -12,8 +12,8 @@ public class InheritanceS3StoreStrategyTests : InheritanceTests public InheritanceS3StoreStrategyTests(ITestOutputHelper outputHelper) : base(outputHelper, - (b, logger) => b.AddS3Strategy(OPTIONS), - (b, logger) => b.AddS3Strategy(OPTIONS)) + (b, logger) => b.AddS3Storage(OPTIONS), + (b, logger) => b.AddS3Storage(OPTIONS)) { } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs index c8d8efef..4dcf841b 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -43,7 +43,7 @@ public MigrationReceiverTest(ITestOutputHelper outputHelper) .AddVoidStrategy(); _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsEnvKeys { Endpoint = SOURCE_KEY }) - .AddS3Strategy(new S3Options { Bucket = "event-source-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); + .AddS3Storage(new S3Options { Bucket = "event-source-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); } #endregion // Ctor diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs index 4ea616ab..b8ec7a04 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyExplicitTests.cs @@ -7,8 +7,8 @@ public class S3StoreStrategyExplicitTests : EndToEndExplicitTests public S3StoreStrategyExplicitTests(ITestOutputHelper outputHelper) : base(outputHelper, - (b, logger) => b.AddS3Strategy(), - (b, logger) => b.AddS3Strategy()) + (b, logger) => b.AddS3Storage(), + (b, logger) => b.AddS3Storage()) { } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs index 3d91ac90..952c9f4e 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyStressTests .cs @@ -12,8 +12,8 @@ public class S3StoreStrategyStressTests : EndToEndStressTests public S3StoreStrategyStressTests(ITestOutputHelper outputHelper) : base(outputHelper, - (b, logger) => b.AddS3Strategy(OPTIONS), - (b, logger) => b.AddS3Strategy(OPTIONS)) + (b, logger) => b.AddS3Storage(OPTIONS), + (b, logger) => b.AddS3Storage(OPTIONS)) { } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs index 6efda25a..05294c89 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreStrategyTests.cs @@ -12,8 +12,8 @@ public class S3StoreStrategyTests : EndToEndTests public S3StoreStrategyTests(ITestOutputHelper outputHelper) : base(outputHelper, - (b, logger) => b.AddS3Strategy(OPTIONS), - (b, logger) => b.AddS3Strategy(OPTIONS)) + (b, logger) => b.AddS3Storage(OPTIONS), + (b, logger) => b.AddS3Storage(OPTIONS)) { } diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj index 59f6fae2..a804c1e1 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj @@ -8,7 +8,7 @@ - + all diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index a8f0618e..bf85fdb8 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -17,14 +17,14 @@ namespace Microsoft.Extensions.Configuration { /// - /// Weknow core extensions for ASP.NET Core + /// core extensions for ASP.NET Core /// public static class AspCoreExtensions { #region AddRedis /// - /// Adds the weknow standard configuration. + /// Adds the standard configuration. /// /// The services. /// The host env. @@ -43,17 +43,17 @@ public static IConnectionMultiplexer AddRedis( #endregion // AddRedis - #region AddOpenTelemetryWeknow + #region AddOpenTelemetry /// - /// Adds the weknow open-telemetry binding. + /// Adds the open-telemetry binding. /// /// The services. /// The host env. /// Short name of the application. /// The redis connection. /// - public static IServiceCollection AddOpenTelemetryWeknow( + public static IServiceCollection AddOpenTelemetry( this IServiceCollection services, IHostEnvironment hostEnv, string shortAppName, @@ -124,7 +124,7 @@ bool OpenTelemetryFilterMap(string? path) #endregion // OpenTelemetryFilter } - #endregion // AddOpenTelemetryWeknow + #endregion // AddOpenTelemetry #region WithJsonOptions @@ -157,7 +157,7 @@ public static JsonSerializerOptions WithDefault(this JsonSerializerOptions optio return options; } - #region UseRestDefaultsWeknow + #region UseRestDefaults /// /// Pre-configured host defaults for rest API. @@ -200,6 +200,6 @@ public static IHostBuilder UseRestDefaults( return builder; } - #endregion // UseRestDefaultsWeknow + #endregion // UseRestDefaults } } diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop b/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop index d61e8ba9..e605f2e9 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop +++ b/Tests/EventSourcing.Backbone.WebEventTest/Dockerfile.develop @@ -6,12 +6,12 @@ ENV DOTNET_USE_POLLING_FILE_WATCHER=true EXPOSE 80 WORKDIR /src -COPY ["Tests/Weknow.EventSourcing.Backbone.WebEventTest/Weknow.EventSourcing.Backbone.WebEventTest.csproj", "Tests/Weknow.EventSourcing.Backbone.WebEventTest/"] +COPY ["Tests/.EventSourcing.Backbone.WebEventTest/.EventSourcing.Backbone.WebEventTest.csproj", "Tests/.EventSourcing.Backbone.WebEventTest/"] -RUN dotnet restore "Tests/Weknow.EventSourcing.Backbone.WebEventTest/Weknow.EventSourcing.Backbone.WebEventTest.csproj" +RUN dotnet restore "Tests/.EventSourcing.Backbone.WebEventTest/.EventSourcing.Backbone.WebEventTest.csproj" COPY . . -WORKDIR "/src/Tests/Weknow.EventSourcing.Backbone.WebEventTest" -RUN dotnet build --no-restore "Weknow.EventSourcing.Backbone.WebEventTest.csproj" -c $BUILD_CONFIGURATION +WORKDIR "/src/Tests/.EventSourcing.Backbone.WebEventTest" +RUN dotnet build --no-restore ".EventSourcing.Backbone.WebEventTest.csproj" -c $BUILD_CONFIGURATION RUN echo "exec dotnet run --no-build --no-launch-profile -c $BUILD_CONFIGURATION --" > /entrypoint.sh diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index adcfa446..37463cd6 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -69,16 +69,13 @@ }); IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); -services.AddOpenTelemetryWeknow(environment, shortAppName, redisConnection); +services.AddOpenTelemetry(environment, shortAppName, redisConnection); services.AddOptions(); // enable usage of IOptionsSnapshot dependency injection - services.AddEventSourceRedisConnection(); - services.AddEventSource(env); - void RedisConfigEnrichment(ConfigurationOptions configuration) { configuration.ReconnectRetryPolicy = new RedisReconnectRetryPolicy(); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 96bc7224..05bc61df 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Configuration { /// - /// Weknow core extensions for ASP.NET Core + /// core extensions for ASP.NET Core /// public static class RegisterEventSourceExtensions { @@ -42,7 +42,7 @@ public static IServiceCollection AddEventSource( IConsumerReadyBuilder consumer = ioc.ResolveRedisConsumerChannel() .ResolveS3Strategy(s3Options) - // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) + // .AddS3Storage(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { TraceAsParent = TimeSpan.FromMinutes(10), @@ -57,7 +57,7 @@ public static IServiceCollection AddEventSource( IConsumerHooksBuilder consumer = ioc.ResolveRedisConsumerChannel() .ResolveS3Strategy(s3Options) - // .AddS3Strategy(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) + // .AddS3Storage(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { TraceAsParent = TimeSpan.FromMinutes(10), diff --git a/Tests/EventSourcing.Backbone.WebEventTest/azds.yaml b/Tests/EventSourcing.Backbone.WebEventTest/azds.yaml index 05947ad4..2a513855 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/azds.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/azds.yaml @@ -4,7 +4,7 @@ build: context: ..\.. dockerfile: Dockerfile install: - chart: charts/weknoweventsourcebackbonewebeventtest + chart: charts/eventsourcebackbonewebeventtest values: - values.dev.yaml? - secrets.dev.yaml? @@ -20,17 +20,17 @@ install: # - name: myRegistryKeySecretName replicaCount: 1 image: - repository: weknoweventsourcebackbonewebeventtest + repository: eventsourcebackbonewebeventtest tag: $(tag) pullPolicy: Never ingress: annotations: kubernetes.io/ingress.class: traefik-azds hosts: - # This expands to form the service's public URL: [space.s.][rootSpace.]weknoweventsourcebackbonewebeventtest...azds.io - # Customize the public URL by changing the 'weknoweventsourcebackbonewebeventtest' text between the $(rootSpacePrefix) and $(hostSuffix) tokens + # This expands to form the service's public URL: [space.s.][rootSpace.]eventsourcebackbonewebeventtest...azds.io + # Customize the public URL by changing the 'eventsourcebackbonewebeventtest' text between the $(rootSpacePrefix) and $(hostSuffix) tokens # For more information see https://aka.ms/devspaces/routing - - $(spacePrefix)$(rootSpacePrefix)weknoweventsourcebackbonewebeventtest$(hostSuffix) + - $(spacePrefix)$(rootSpacePrefix)eventsourcebackbonewebeventtest$(hostSuffix) configurations: develop: build: @@ -46,6 +46,6 @@ configurations: - "!**/*.{sln,csproj}" command: [dotnet, run, --no-restore, --no-build, --no-launch-profile, -c, "${BUILD_CONFIGURATION:-Debug}"] iterate: - processesToKill: [dotnet, vsdbg, Weknow.EventSourcing.Backbone.WebEventTest] + processesToKill: [dotnet, vsdbg, .EventSourcing.Backbone.WebEventTest] buildCommands: - [dotnet, build, --no-restore, -c, "${BUILD_CONFIGURATION:-Debug}"] diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml index b6dcbff9..a4a04851 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 appVersion: "1.0" description: A Helm chart for Kubernetes -name: weknoweventsourcebackbonewebeventtest +name: eventsourcebackbonewebeventtest version: 0.1.0 diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt index d13c4600..210bbe59 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/NOTES.txt @@ -4,16 +4,16 @@ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "weknoweventsourcebackbonewebeventtest.fullname" . }}) + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "eventsourcebackbonewebeventtest.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "weknoweventsourcebackbonewebeventtest.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "weknoweventsourcebackbonewebeventtest.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + You can watch the status of by running 'kubectl get svc -w {{ template "eventsourcebackbonewebeventtest.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "eventsourcebackbonewebeventtest.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "weknoweventsourcebackbonewebeventtest.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "eventsourcebackbonewebeventtest.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl port-forward $POD_NAME 8080:80 {{- end }} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl index e534ff90..2242ea66 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/_helpers.tpl @@ -2,7 +2,7 @@ {{/* Expand the name of the chart. */}} -{{- define "weknoweventsourcebackbonewebeventtest.name" -}} +{{- define "eventsourcebackbonewebeventtest.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} @@ -11,7 +11,7 @@ Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "weknoweventsourcebackbonewebeventtest.fullname" -}} +{{- define "eventsourcebackbonewebeventtest.fullname" -}} {{- if .Values.fullnameOverride -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} @@ -27,6 +27,6 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "weknoweventsourcebackbonewebeventtest.chart" -}} +{{- define "eventsourcebackbonewebeventtest.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- end -}} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml index f2989b75..912068f2 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/deployment.yaml @@ -1,10 +1,10 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ template "weknoweventsourcebackbonewebeventtest.fullname" . }} + name: {{ template "eventsourcebackbonewebeventtest.fullname" . }} labels: - app: {{ template "weknoweventsourcebackbonewebeventtest.name" . }} - chart: {{ template "weknoweventsourcebackbonewebeventtest.chart" . }} + app: {{ template "eventsourcebackbonewebeventtest.name" . }} + chart: {{ template "eventsourcebackbonewebeventtest.chart" . }} draft: {{ .Values.draft | default "draft-app" }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} @@ -13,12 +13,12 @@ spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: - app: {{ template "weknoweventsourcebackbonewebeventtest.name" . }} + app: {{ template "eventsourcebackbonewebeventtest.name" . }} release: {{ .Release.Name }} template: metadata: labels: - app: {{ template "weknoweventsourcebackbonewebeventtest.name" . }} + app: {{ template "eventsourcebackbonewebeventtest.name" . }} draft: {{ .Values.draft | default "draft-app" }} release: {{ .Release.Name }} annotations: @@ -49,7 +49,7 @@ spec: - name: {{ $ref }}_{{ $key }} valueFrom: secretKeyRef: - name: {{ template "weknoweventsourcebackbonewebeventtest.fullname" $root }}-{{ $ref | lower }} + name: {{ template "eventsourcebackbonewebeventtest.fullname" $root }}-{{ $ref | lower }} key: {{ $key }} {{- end }} {{- end }} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml index 2a3fa054..db263a4b 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/ingress.yaml @@ -1,5 +1,5 @@ {{- if .Values.ingress.enabled -}} -{{- $fullName := include "weknoweventsourcebackbonewebeventtest.fullname" . -}} +{{- $fullName := include "eventsourcebackbonewebeventtest.fullname" . -}} {{- $servicePort := .Values.service.port -}} {{- $ingressPath := .Values.ingress.path -}} {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} @@ -11,8 +11,8 @@ kind: Ingress metadata: name: {{ $fullName }} labels: - app: {{ template "weknoweventsourcebackbonewebeventtest.name" . }} - chart: {{ template "weknoweventsourcebackbonewebeventtest.chart" . }} + app: {{ template "eventsourcebackbonewebeventtest.name" . }} + chart: {{ template "eventsourcebackbonewebeventtest.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} {{- with .Values.ingress.annotations }} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml index e030e892..a59c70bd 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/secrets.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ template "weknoweventsourcebackbonewebeventtest.fullname" $root }}-{{ $name | lower }} + name: {{ template "eventsourcebackbonewebeventtest.fullname" $root }}-{{ $name | lower }} data: {{- range $key, $value := $values }} {{ $key }}: {{ $value | b64enc }} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml index 010f382a..1423ff07 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/templates/service.yaml @@ -1,10 +1,10 @@ apiVersion: v1 kind: Service metadata: - name: {{ template "weknoweventsourcebackbonewebeventtest.fullname" . }} + name: {{ template "eventsourcebackbonewebeventtest.fullname" . }} labels: - app: {{ template "weknoweventsourcebackbonewebeventtest.name" . }} - chart: {{ template "weknoweventsourcebackbonewebeventtest.chart" . }} + app: {{ template "eventsourcebackbonewebeventtest.name" . }} + chart: {{ template "eventsourcebackbonewebeventtest.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} spec: @@ -15,5 +15,5 @@ spec: protocol: TCP name: http selector: - app: {{ template "weknoweventsourcebackbonewebeventtest.name" . }} + app: {{ template "eventsourcebackbonewebeventtest.name" . }} release: {{ .Release.Name }} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml index d6a75a1d..320adc5b 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml +++ b/Tests/EventSourcing.Backbone.WebEventTest/charts/weknoweventsourcebackbonewebeventtest/values.yaml @@ -1,10 +1,10 @@ -# Default values for weknoweventsourcebackbonewebeventtest. +# Default values for eventsourcebackbonewebeventtest. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -fullnameOverride: weknoweventsourcebackbonewebeventtest +fullnameOverride: eventsourcebackbonewebeventtest replicaCount: 1 image: - repository: weknoweventsourcebackbonewebeventtest + repository: eventsourcebackbonewebeventtest tag: stable pullPolicy: IfNotPresent imagePullSecrets: [] diff --git a/Tests/WebSampleS3/Controllers/ConsumerController.cs b/Tests/WebSampleS3/Controllers/ConsumerController.cs new file mode 100644 index 00000000..2dd4775b --- /dev/null +++ b/Tests/WebSampleS3/Controllers/ConsumerController.cs @@ -0,0 +1,37 @@ +using System.Text.Json; + +using EventSourcing.Backbone; +using WebSampleS3; + +using Microsoft.AspNetCore.Mvc; + +namespace WebSampleS3.Controllers; + +[ApiController] +[Route("[controller]")] +public class ConsumerController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IConsumerReceiver _receiver; + + public ConsumerController( + ILogger logger, + IConsumerReadyBuilder consumerBuilder) + { + _logger = logger; + _receiver = consumerBuilder.BuildReceiver(); + } + + /// + /// Gets an event by event key. + /// + /// The event key. + /// + [HttpGet("{eventKey}")] + public async Task GetAsync(string eventKey) + { + _logger.LogDebug("fetching event [{key}]", eventKey); + var json = await _receiver.GetJsonByIdAsync(eventKey); + return json; + } +} \ No newline at end of file diff --git a/Tests/WebSampleS3/Controllers/ProducerController.cs b/Tests/WebSampleS3/Controllers/ProducerController.cs new file mode 100644 index 00000000..d56e4888 --- /dev/null +++ b/Tests/WebSampleS3/Controllers/ProducerController.cs @@ -0,0 +1,86 @@ +using EventSourcing.Backbone; +using WebSampleS3; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; + +namespace WebSampleS3.Controllers; + +public record Ord (User user, Product Product); + +[ApiController] +[Route("[controller]")] +public class ProducerController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IShipmentTrackingProducer _producer; + + public ProducerController( + ILogger logger, + IShipmentTrackingProducer producer) + { + _logger = logger; + _producer = producer; + } + + /// + /// Post order state. + /// + /// The event key. + /// + [HttpPost("order-placed")] + [ProducesResponseType(StatusCodes.Status201Created)] + [AllowAnonymous] + public async Task PostOrderPlacedAsync(Ord payload) + { + var (user, product) = payload; + _logger.LogDebug("Sending order-placed event"); + EventKey id = await _producer.OrderPlacedAsync(user, product, DateTimeOffset.Now); + return id; + } + + /// + /// Post packing state. + /// + /// The event key. + /// + [HttpPost("packing")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task PostPackingAsync([FromBody](string email, int productId) payload) + { + var (email, productId) = payload; + _logger.LogDebug("Sending packing event"); + EventKey id = await _producer.PackingAsync(email, productId, DateTimeOffset.Now); + return id; + } + + /// + /// Post on-delivery state. + /// + /// The event key. + /// + [HttpPost("on-delivery")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task PostOnDeliveryAsync([FromBody](string email, int productId) payload) + { + var (email, productId) = payload; + _logger.LogDebug("Sending on-delivery event"); + EventKey id = await _producer.OnDeliveryAsync(email, productId, DateTimeOffset.Now); + return id; + } + + /// + /// Post on-received state. + /// + /// The event key. + /// + [HttpPost("on-received")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task PostOnReceivedAsync([FromBody](string email, int productId) payload) + { + var (email, productId) = payload; + _logger.LogDebug("Sending on-received event"); + EventKey id = await _producer.OnReceivedAsync(email, productId, DateTimeOffset.Now); + return id; + } +} \ No newline at end of file diff --git a/Tests/WebSampleS3/Entities/Constants.cs b/Tests/WebSampleS3/Entities/Constants.cs new file mode 100644 index 00000000..ff6067b0 --- /dev/null +++ b/Tests/WebSampleS3/Entities/Constants.cs @@ -0,0 +1,40 @@ +using Amazon.Runtime.CredentialManagement; +using Amazon.Runtime; +using Amazon.S3; + +using EventSourcing.Backbone; +using Amazon; +using System.Net.Sockets; + +namespace WebSampleS3; + +public class Constants +{ + public const string URI = "hello-event-sourcing"; + public const string S3_BUCKET = "event-sourcing-demo"; + //public const string S3_ACCESS_KEY_ENV = "S3_EVENT_DEMO_ACCESS_KEY"; + //public const string S3_SECRET_ENV = "S3_EVENT_DEMO_SECRET"; + //public const string S3_REGION_ENV = "S3_EVENT_DEMO_REGION"; + + //public static IAmazonS3 CreateS3Client(RegionEndpoint region, string profile = "playground") + public static IAmazonS3 CreateS3Client(RegionEndpoint region, string profile = "playground") + { + var chain = new CredentialProfileStoreChain(); + AWSCredentials awsCredentials; + if (chain.TryGetAWSCredentials(profile, out awsCredentials)) + { + // Use awsCredentials to create an Amazon S3 service client + var client = new AmazonS3Client(awsCredentials, region); + return client; + } + + var s3Client = EventSourcing.Backbone.Channels.S3RepositoryFactory.CreateClient(); + return s3Client; + } + + public static readonly S3Options S3Options = new S3Options + { + Bucket = Constants.S3_BUCKET + }; + +} diff --git a/Tests/WebSampleS3/Entities/IShipmentTracking.cs b/Tests/WebSampleS3/Entities/IShipmentTracking.cs new file mode 100644 index 00000000..645a8610 --- /dev/null +++ b/Tests/WebSampleS3/Entities/IShipmentTracking.cs @@ -0,0 +1,22 @@ +#pragma warning disable S1133 // Deprecated code should be removed + +using EventSourcing.Backbone; + +namespace WebSampleS3; + + +/// +/// Event's schema definition +/// Return type of each method should be +/// +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +[Obsolete("Either use the Producer or Consumer version of this interface", true)] +public interface IShipmentTracking +{ + // the following method will be ValueTask OrderPlacedAsync(...) at the producer interface and ValueTask OrderPlacedAsync(...) at the consumer + void OrderPlaced(User user, Product product, DateTimeOffset time); + ValueTask PackingAsync(string email, int productId, DateTimeOffset time); + ValueTask OnDeliveryAsync(string email, int productId, DateTimeOffset time); + ValueTask OnReceivedAsync(string email, int productId, DateTimeOffset time); +} \ No newline at end of file diff --git a/Tests/WebSampleS3/Entities/Product.cs b/Tests/WebSampleS3/Entities/Product.cs new file mode 100644 index 00000000..08d7313d --- /dev/null +++ b/Tests/WebSampleS3/Entities/Product.cs @@ -0,0 +1,3 @@ +namespace WebSampleS3; + +public record Product(int id, string name, double price); diff --git a/Tests/WebSampleS3/Entities/User.cs b/Tests/WebSampleS3/Entities/User.cs new file mode 100644 index 00000000..7f8d3d61 --- /dev/null +++ b/Tests/WebSampleS3/Entities/User.cs @@ -0,0 +1,3 @@ +namespace WebSampleS3; + +public record User(int id, string email, string name); diff --git a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs new file mode 100644 index 00000000..c2d67284 --- /dev/null +++ b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs @@ -0,0 +1,47 @@ +using EventSourcing.Backbone; +using WebSampleS3; + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace WebSample.Extensions +{ + /// + /// DI Extensions for ASP.NET Core + /// + public static class ConsumerExtensions + { + /// + /// Adds the shipment tracking producer. + /// + /// The services. + /// The URI. + /// The environment. + /// + public static IServiceCollection AddShipmentTrackingConsumer + ( + this IServiceCollection services, + string uri, + string s3Bucket, + Env env) + { + var s3Options = new S3Options { Bucket = s3Bucket }; + services.AddSingleton(ioc => + { + IConsumerReadyBuilder consumer = + ioc.ResolveRedisConsumerChannel() + .ResolveS3Strategy(s3Options) + .WithOptions(o => o with + { + TraceAsParent = TimeSpan.FromMinutes(10), + OriginFilter = MessageOrigin.Original + }) + .Environment(env) + .Uri(uri); + return consumer; + }); + + return services; + } + } +} diff --git a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs new file mode 100644 index 00000000..99863091 --- /dev/null +++ b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs @@ -0,0 +1,153 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +using EventSourcing.Backbone; + +using Microsoft.AspNetCore.Server.Kestrel.Core; + +using OpenTelemetry.Exporter; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +using StackExchange.Redis; + +using WebSample.Extensions; + + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace WebSample.Extensions +{ + /// + /// open telemetry extensions for ASP.NET Core + /// + public static class OpenTelemetryExtensions + { + #region AddOpenTelemetry + + /// + /// Adds the open-telemetry binding. + /// + /// The services. + /// The host env. + /// Short name of the application. + /// An open telemetry sampler. + /// The telemetry path filter. + /// + public static IServiceCollection AddOpenTelemetry( + this IServiceCollection services, + IHostEnvironment hostEnv, + string appName, + Sampler? sampler = null, + Func? telemetryPathFilter = null) + { + // see: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + + Console.WriteLine($"JAEGER endpoint: key='OTEL_EXPORTER_JAEGER_ENDPOINT', env='{hostEnv.EnvironmentName}'"); // will be visible in the pods logs + +#pragma warning disable S125 // Sections of code should not be commented out + services.AddOpenTelemetry() + .WithTracing(builder => + { + TracerProviderBuilder tracerProviderBuilder = + builder.SetResourceBuilder(ResourceBuilder.CreateDefault() + .AddService(appName)) + .ListenToEventSourceRedisChannel() + .AddAspNetCoreInstrumentation(m => + { + m.Filter = OpenTelemetryFilter; + // m.Enrich + m.RecordException = true; + m.EnableGrpcAspNetCoreSupport = true; + }) + .AddHttpClientInstrumentation(m => + { + // m.Enrich + m.RecordException = true; + }) + //.AddRedisInstrumentation(redisConnection + // //, m => { + // // m.FlushInterval + // //} + // ) + .AddOtlpExporter(); + if (sampler == null) + tracerProviderBuilder.SetSampler(); + else + tracerProviderBuilder.SetSampler(sampler); + + if (hostEnv.IsDevelopment()) + { + builder.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Console); + } + }); + return services; +#pragma warning restore S125 // Sections of code should not be commented out + + #region OpenTelemetryFilter + + bool OpenTelemetryFilter(HttpContext context) + { + string? path = context.Request.Path.Value; + return telemetryPathFilter?.Invoke(path) ?? OpenTelemetryFilterMap(path); + } + + bool OpenTelemetryFilterMap(string? path) + { + if (string.IsNullOrEmpty(path) || + path == "/health" || + path == "/readiness" || + path == "/version" || + path == "/settings" || + path == "/api/v2/write" || // influx metrics + path == "/_bulk" || + path.StartsWith("/swagger") || + path.IndexOf("health-check") != -1) + { + return false; + } + return true; + } + + #endregion // OpenTelemetryFilter + } + + #endregion // AddOpenTelemetry + + #region WithJsonOptions + + /// + /// Set Controller's with the standard json configuration. + /// + /// The controllers. + /// + public static IMvcBuilder WithJsonOptions( + this IMvcBuilder controllers) + { + return controllers.AddJsonOptions(options => + { + // https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to + JsonSerializerOptions setting = options.JsonSerializerOptions; + setting.WithDefault(); + + }); + } + + #endregion // WithJsonOptions + + #region WithDefault + + public static JsonSerializerOptions WithDefault(this JsonSerializerOptions options) + { + options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + options.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; + options.WriteIndented = true; + options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); + options.Converters.Add(JsonMemoryBytesConverterFactory.Default); + return options; + } + + #endregion // WithDefault + } +} diff --git a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs new file mode 100644 index 00000000..6c9fa02c --- /dev/null +++ b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs @@ -0,0 +1,43 @@ +using EventSourcing.Backbone; +using WebSampleS3; + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace WebSampleS3; + +/// +/// DI Extensions for ASP.NET Core +/// +public static class ShipmentTrackingProducerExtensions +{ + /// + /// Adds the shipment tracking producer. + /// + /// The services. + /// The URI. + /// The environment. + /// + public static IServiceCollection AddShipmentTrackingProducer + ( + this IServiceCollection services, + string uri, + string s3Bucket, + Env env) + { + var s3Options = new S3Options { Bucket = s3Bucket }; + services.AddSingleton(ioc => + { + ILogger logger = ioc.GetService>() ?? throw new EventSourcingException("Logger is missing"); + IShipmentTrackingProducer producer = ioc.ResolveRedisProducerChannel() + .ResolveS3Strategy(s3Options) + .Environment(env) + .Uri(uri) + .WithLogger(logger) + .BuildShipmentTrackingProducer(); + return producer; + }); + + return services; + } +} diff --git a/Tests/WebSampleS3/Jobs/ConsumerJob.cs b/Tests/WebSampleS3/Jobs/ConsumerJob.cs new file mode 100644 index 00000000..5b0564e9 --- /dev/null +++ b/Tests/WebSampleS3/Jobs/ConsumerJob.cs @@ -0,0 +1,180 @@ +using System.Text.Json; +using System.Threading; + +using EventSourcing.Backbone; +using EventSourcing.Backbone.Building; + +using Microsoft.Extensions.Hosting; + +namespace WebSampleS3; + +/// +/// Consumer job +/// +/// +/// +public sealed class ConsumerJob : IHostedService, IAsyncDisposable +{ + private readonly IConsumerSubscribeBuilder _builder; + private CancellationTokenSource? _cancellationTokenSource; + private readonly IShipmentTrackingConsumer _subscriber; + private IConsumerLifetime? _subscription; + //private const string CONSUMER_GROUP = "CONSUMER"; + + #region Ctor + + /// + /// Initializes a new instance. + /// + /// The logger. + /// The builder. + /// The producer. + public ConsumerJob( + ILogger logger, + IConsumerReadyBuilder consumerBuilder) + { + _builder = consumerBuilder.WithLogger(logger); + _subscriber = new Subscriber(logger); + } + + #endregion Ctor + + #region OnStartAsync + + /// + /// Start Consumer Job. + /// + /// The cancellation token. + Task IHostedService.StartAsync(CancellationToken cancellationToken) + { + _cancellationTokenSource = new CancellationTokenSource(); + var canellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); + _subscription = _builder + // .Group(CONSUMER_GROUP) + .WithCancellation(canellation.Token) + // this extension is generate (if you change the interface use the correlated new generated extension method) + .SubscribeShipmentTrackingConsumer(_subscriber); + + return Task.CompletedTask; + //await _subscription.Completion; + } + + #endregion // OnStartAsync + + #region StopAsync + + /// + /// Stops the Consumer Job. + /// + /// The cancellation token. + /// + async Task IHostedService.StopAsync(CancellationToken cancellationToken) + { + _cancellationTokenSource?.CancelSafe(); + await (_subscription?.Completion ?? Task.CompletedTask); + } + + #endregion // StopAsync + + #region DisposeAsync + + /// + /// Disposes the asynchronous. + /// + /// + async ValueTask IAsyncDisposable.DisposeAsync() + { + _cancellationTokenSource?.CancelSafe(); + await (_subscription?.Completion ?? Task.CompletedTask); + } + + #endregion // DisposeAsync + + #region class Subscriber : IShipmentTrackingConsumer + + /// + /// The subscriber implementation + /// + /// + private sealed class Subscriber : IShipmentTrackingConsumer + { + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public Subscriber( + //ConsumerMetadata metadata, + ILogger logger) + { + _logger = logger; + } + + /// + /// Handle [OrderPlaced] event. + /// + /// The user. + /// The product. + /// The time. + /// + ValueTask IShipmentTrackingConsumer.OrderPlacedAsync(User user, Product product, DateTimeOffset time) + { + // get the current event metadata + Metadata? meta = ConsumerMetadata.Context; + + _logger.LogInformation("handling OrderPlaced [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, user.email, product.id, time); + return ValueTask.CompletedTask; + } + + /// + /// Handle [Packings] event. + /// + /// The email. + /// The product identifier. + /// The time. + /// + ValueTask IShipmentTrackingConsumer.PackingAsync(string email, int productId, DateTimeOffset time) + { + // get the current event metadata + Metadata? meta = ConsumerMetadata.Context; + + _logger.LogInformation("handling Packing [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); + return ValueTask.CompletedTask; + } + + /// + /// Handle [on-delivery] event. + /// + /// The email. + /// The product identifier. + /// The time. + /// + ValueTask IShipmentTrackingConsumer.OnDeliveryAsync(string email, int productId, DateTimeOffset time) + { + // get the current event metadata + Metadata? meta = ConsumerMetadata.Context; + + _logger.LogInformation("handling OnDelivery [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); + return ValueTask.CompletedTask; + } + + /// + /// Handle [on-received] event. + /// + /// The email. + /// The product identifier. + /// The time. + /// + ValueTask IShipmentTrackingConsumer.OnReceivedAsync(string email, int productId, DateTimeOffset time) + { + // get the current event metadata + Metadata? meta = ConsumerMetadata.Context; + + _logger.LogInformation("handling OnReceived [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); + return ValueTask.CompletedTask; + } + } + + #endregion // class Subscriber : IShipmentTrackingConsumer +} diff --git a/Tests/WebSampleS3/Program.cs b/Tests/WebSampleS3/Program.cs new file mode 100644 index 00000000..fe2518ce --- /dev/null +++ b/Tests/WebSampleS3/Program.cs @@ -0,0 +1,54 @@ +using Amazon.S3; + +using StackExchange.Redis; + +using WebSample; +using WebSample.Extensions; + +using WebSampleS3; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); +builder.Services.AddAWSService(); +builder.Services.AddEventSourceRedisConnection(); + +// Add services to the container. + +IWebHostEnvironment environment = builder.Environment; +string env = environment.EnvironmentName; +string appName = environment.ApplicationName; + +builder.Services.AddOpenTelemetry(environment, appName); +builder.Services.AddEventSourceRedisConnection(); + +string URI = "shipment-tracking"; +// make sure to create the bucket on AWS S3 with both prefix 'dev.' and 'prod.' and any other environment you're using (like staging,etc.) +string s3Bucket = "shipment-tracking-sample"; + +builder.Services.AddShipmentTrackingProducer(URI, s3Bucket, env); +builder.Services.AddShipmentTrackingConsumer(URI, s3Bucket, env); + +builder.Services.AddHostedService(); + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/Tests/WebSampleS3/Properties/launchSettings.json b/Tests/WebSampleS3/Properties/launchSettings.json new file mode 100644 index 00000000..adc338eb --- /dev/null +++ b/Tests/WebSampleS3/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:49788", + "sslPort": 44330 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5280", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7204;http://localhost:5280", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj new file mode 100644 index 00000000..c7bcd45b --- /dev/null +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -0,0 +1,50 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/WebSampleS3/appsettings.Development.json b/Tests/WebSampleS3/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Tests/WebSampleS3/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Tests/WebSampleS3/appsettings.json b/Tests/WebSampleS3/appsettings.json new file mode 100644 index 00000000..58dcdba5 --- /dev/null +++ b/Tests/WebSampleS3/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "AWS": { + "Region": "us-east-1", + "Profile": "playground" + } +} diff --git a/Tests/WebSampleS3/icon.png b/Tests/WebSampleS3/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 Date: Mon, 5 Jun 2023 11:43:58 +0000 Subject: [PATCH 099/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index ad48aabb..d1bcfb0a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.84 + 1.2.85 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 685a0e4c61821ef6b545ac37c760924998d4faff Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:44:04 +0300 Subject: [PATCH 100/178] build: add test project --- EventSourcing.Backbone.sln | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index aee46ab0..7414dec4 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -64,9 +64,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsumingEvents", "Tests\He EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProducingEvents", "Tests\HelloWorld\ProducingEvents\ProducingEvents.csproj", "{B528D83E-1AB9-4461-BA5E-993FF882F85B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebS3Sample", "Tests\WebS3Sample\WebS3Sample.csproj", "{4B0F7B88-F478-4577-B35E-647AA539A7F4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSampleS3", "Tests\WebSampleS3\WebSampleS3.csproj", "{7A749C43-60F4-4DDC-894C-5E407B08A3A9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSampleS3", "Tests\WebSampleS3\WebSampleS3.csproj", "{7A749C43-60F4-4DDC-894C-5E407B08A3A9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -171,12 +169,6 @@ Global {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Release|Any CPU.ActiveCfg = Release|Any CPU {B528D83E-1AB9-4461-BA5E-993FF882F85B}.Release|Any CPU.Build.0 = Release|Any CPU - {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Gen|Any CPU.Build.0 = Gen|Any CPU - {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4B0F7B88-F478-4577-B35E-647AA539A7F4}.Release|Any CPU.Build.0 = Release|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Gen|Any CPU.ActiveCfg = Gen|Any CPU @@ -208,7 +200,6 @@ Global {81EBE4CC-2E70-4E66-89DC-DCBB591343AB} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {A78DF256-6896-4246-8CF5-8CCBB27760A5} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {B528D83E-1AB9-4461-BA5E-993FF882F85B} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} - {4B0F7B88-F478-4577-B35E-647AA539A7F4} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} {7A749C43-60F4-4DDC-894C-5E407B08A3A9} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution From 1c16e7350448a9b499605edabe3c2c7df3ffe95d Mon Sep 17 00:00:00 2001 From: CI/CD Date: Mon, 5 Jun 2023 11:44:14 +0000 Subject: [PATCH 101/178] Increment Version --- Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d1bcfb0a..92d958aa 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.2.85 + 1.2.86 - 1.2.85: + 1.2.86: Breaking changes: S3Strategy was renamed to S3Storage @@ -46,7 +46,7 @@ - + From ca63770f6c7c7da06911f8afb5e15f4392700e7b Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:54:33 +0300 Subject: [PATCH 102/178] rfc: clean up --- .../EventSourceRedisConnectionFactory.cs | 6 ++---- .../RedisCommonProviderExtensions.cs | 3 +-- .../RedisDiExtensions.cs | 4 ++-- .../S3ConsumerStorageStrategy.cs | 3 +-- .../S3ConsumerStorageStrategyExtension.cs | 6 ++---- .../S3ProducerStorageStrategyExtension.cs | 7 ++----- .../S3Options.cs | 4 +--- .../S3RepositoryFactory.cs | 3 +-- .../Consumer/Builder/IConsumerBuilder.cs | 4 +--- .../Producer/Builder/IProducerBuilder.cs | 4 +--- .../Builder/ProducerBuilder.cs | 2 +- .../InheritanceTests.cs | 2 +- .../S3StoreCredentialsTests.cs | 20 +++++++++---------- .../Contracts/IS3Test.cs | 5 +---- .../RedisReconnectRetryPolicy.cs | 5 ++++- Tests/HelloWorld/ConsumingEvents/Program.cs | 4 ---- Tests/HelloWorld/EventsAbstractions/URIs.cs | 8 +------- Tests/HelloWorld/ProducingEvents/Program.cs | 2 -- .../Controllers/ConsumerController.cs | 1 - .../Controllers/ProducerController.cs | 20 +++++++++---------- Tests/WebSampleS3/Entities/Constants.cs | 11 +++------- .../Extensions/ConsumerExtensions.cs | 2 +- .../Extensions/OpenTelemetryExtensions.cs | 4 ---- .../ShipmentTrackingProducerExtensions.cs | 2 +- Tests/WebSampleS3/Jobs/ConsumerJob.cs | 15 +++++--------- Tests/WebSampleS3/Program.cs | 4 ---- .../RoslynHelper.cs | 8 ++++---- 27 files changed, 55 insertions(+), 104 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs index 598a3938..aadba8fa 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs @@ -1,6 +1,4 @@ -using System.Net; - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -39,7 +37,7 @@ public class EventSourceRedisConnectionFactory : IEventSourceRedisConnectionFact public EventSourceRedisConnectionFactory( ILogger logger, ConfigurationOptions? configuration = null) - : this ((ILogger)logger, configuration) + : this((ILogger)logger, configuration) { } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 72785ce7..e13f742c 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using StackExchange.Redis; diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs index 7669f5f3..01cb70b9 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisDiExtensions.cs @@ -21,7 +21,7 @@ public static IServiceCollection AddEventSourceRedisConnection( this IServiceCollection services) { services.AddSingleton(); - + return services; } @@ -70,7 +70,7 @@ public static IServiceCollection AddEventSourceRedisConnectionFromEnv( services.AddSingleton( sp => { - ILogger logger = sp.GetService>() ?? + ILogger logger = sp.GetService>() ?? throw new EventSourcingException( $"{nameof(AddEventSourceRedisConnectionFromEnv)}: Cannot resolve a logger"); diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs index ba79b759..427499ee 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Text.Json; +using System.Text.Json; using EventSourcing.Backbone.Channels; diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 3eefd54e..c6d37843 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -1,12 +1,10 @@ - -using Amazon.Runtime; -using Amazon.S3; +using Amazon.S3; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace EventSourcing.Backbone diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index a1071aa7..d1fb8d46 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -1,12 +1,9 @@ - -using Amazon.Runtime; -using Amazon.S3; +using Amazon.S3; using EventSourcing.Backbone.Channels; -using Microsoft.Extensions.Logging; - using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace EventSourcing.Backbone { diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs index a106a8b9..077f5e46 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Options.cs @@ -1,6 +1,4 @@ -using Amazon.S3; - -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone { ///

/// S3 provider options diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs index d3b3a061..6b6bd9ab 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3RepositoryFactory.cs @@ -1,7 +1,6 @@ using System.Collections.Concurrent; using Amazon; -using Amazon.Runtime; using Amazon.S3; using Microsoft.Extensions.Logging; @@ -35,7 +34,7 @@ public static IAmazonS3 CreateClient( bool fromEnvironment = true) { accessKey = - fromEnvironment + fromEnvironment ? Environment.GetEnvironmentVariable(accessKey) ?? accessKey : accessKey; secret = diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs index 265e86ae..44cb58b9 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Builder/IConsumerBuilder.cs @@ -1,6 +1,4 @@ -using System; - -using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Building; using Microsoft.Extensions.Logging; diff --git a/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs b/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs index d7d1ab1e..47cbaafe 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Builder/IProducerBuilder.cs @@ -1,6 +1,4 @@ -using System; - -using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Building; using Microsoft.Extensions.Logging; diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs index ba21c4fc..ec0335ea 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/ProducerBuilder.cs @@ -119,7 +119,7 @@ IProducerIocStoreStrategyBuilder IProducerBuilder.UseChannel( /// /// Gets the service provider. /// - IServiceProvider IProducerIocStoreStrategyBuilder.ServiceProvider => + IServiceProvider IProducerIocStoreStrategyBuilder.ServiceProvider => Plan?.ServiceProvider ?? throw new EventSourcingException("ServiceProvider is null"); #endregion // ServiceProvider diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index f3a6bd17..6980741b 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -7,11 +7,11 @@ using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider; using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.UnitTests.Entities; using FakeItEasy; using Microsoft.Extensions.Logging; -using EventSourcing.Backbone.UnitTests.Entities; using StackExchange.Redis; diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs index 88258445..745f4532 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs @@ -1,16 +1,16 @@ -using Xunit.Abstractions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Amazon.S3; + using EventSourcing.Backbone.Building; -using Amazon.Runtime.Internal.Util; +using EventSourcing.Backbone.UnitTests.Entities; + using FakeItEasy; -using StackExchange.Redis; -using System.Threading; -using System; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + using Xunit; -using EventSourcing.Backbone.UnitTests.Entities; +using Xunit.Abstractions; namespace EventSourcing.Backbone.Tests { @@ -47,7 +47,7 @@ public S3StoreCredentialsTests(ITestOutputHelper outputHelper) services.AddSingleton(ioc => { var logger = ioc.GetService>(); - IProducerHooksBuilder producer = ioc.ResolveRedisProducerChannel() + IProducerHooksBuilder producer = ioc.ResolveRedisProducerChannel() .ResolveS3Strategy(s3Options) .WithLogger(logger!) .Uri(TEST_URI); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs index 1f091c94..735b640d 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Contracts/IS3Test.cs @@ -1,7 +1,4 @@ -using System.ComponentModel; -using System.Text.Json; - -namespace EventSourcing.Backbone.WebEventTest +namespace EventSourcing.Backbone.WebEventTest { [EventsContract(EventsContractType.Consumer)] [EventsContract(EventsContractType.Producer)] diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs b/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs index 05d67930..3a9fcbf6 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RedisReconnectRetryPolicy.cs @@ -1,8 +1,11 @@ using StackExchange.Redis; + +namespace EventSourcing.Backbone.WebEventTest; + /// /// Redis reconnect retry policy /// -/// +/// public class RedisReconnectRetryPolicy : IReconnectRetryPolicy { /// diff --git a/Tests/HelloWorld/ConsumingEvents/Program.cs b/Tests/HelloWorld/ConsumingEvents/Program.cs index ae6713f0..f1310570 100644 --- a/Tests/HelloWorld/ConsumingEvents/Program.cs +++ b/Tests/HelloWorld/ConsumingEvents/Program.cs @@ -1,10 +1,6 @@ using EventsAbstractions; using EventSourcing.Backbone; -using StackExchange.Redis; -using System; -using System.Collections.Concurrent; -using System.Drawing; Console.WriteLine("Consuming Events"); diff --git a/Tests/HelloWorld/EventsAbstractions/URIs.cs b/Tests/HelloWorld/EventsAbstractions/URIs.cs index c63b3b67..e6d816a3 100644 --- a/Tests/HelloWorld/EventsAbstractions/URIs.cs +++ b/Tests/HelloWorld/EventsAbstractions/URIs.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace EventsAbstractions +namespace EventsAbstractions { public class URIs { diff --git a/Tests/HelloWorld/ProducingEvents/Program.cs b/Tests/HelloWorld/ProducingEvents/Program.cs index 10adcb00..e5e9110b 100644 --- a/Tests/HelloWorld/ProducingEvents/Program.cs +++ b/Tests/HelloWorld/ProducingEvents/Program.cs @@ -1,8 +1,6 @@ using EventsAbstractions; using EventSourcing.Backbone; - -using System; #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. Console.WriteLine("Producing events"); diff --git a/Tests/WebSampleS3/Controllers/ConsumerController.cs b/Tests/WebSampleS3/Controllers/ConsumerController.cs index 2dd4775b..c1a43d14 100644 --- a/Tests/WebSampleS3/Controllers/ConsumerController.cs +++ b/Tests/WebSampleS3/Controllers/ConsumerController.cs @@ -1,7 +1,6 @@ using System.Text.Json; using EventSourcing.Backbone; -using WebSampleS3; using Microsoft.AspNetCore.Mvc; diff --git a/Tests/WebSampleS3/Controllers/ProducerController.cs b/Tests/WebSampleS3/Controllers/ProducerController.cs index d56e4888..280150d6 100644 --- a/Tests/WebSampleS3/Controllers/ProducerController.cs +++ b/Tests/WebSampleS3/Controllers/ProducerController.cs @@ -1,12 +1,10 @@ using EventSourcing.Backbone; -using WebSampleS3; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Authorization; namespace WebSampleS3.Controllers; -public record Ord (User user, Product Product); +public record Ord(User user, Product Product); [ApiController] [Route("[controller]")] @@ -26,11 +24,11 @@ public ProducerController( /// /// Post order state. /// - /// The event key. + /// The payload. /// [HttpPost("order-placed")] [ProducesResponseType(StatusCodes.Status201Created)] - [AllowAnonymous] + //[AllowAnonymous] public async Task PostOrderPlacedAsync(Ord payload) { var (user, product) = payload; @@ -42,11 +40,11 @@ public async Task PostOrderPlacedAsync(Ord payload) /// /// Post packing state. /// - /// The event key. + /// The payload. /// [HttpPost("packing")] [ProducesResponseType(StatusCodes.Status201Created)] - public async Task PostPackingAsync([FromBody](string email, int productId) payload) + public async Task PostPackingAsync([FromBody] (string email, int productId) payload) { var (email, productId) = payload; _logger.LogDebug("Sending packing event"); @@ -57,11 +55,11 @@ public async Task PostPackingAsync([FromBody](string email, int productI /// /// Post on-delivery state. /// - /// The event key. + /// The payload. /// [HttpPost("on-delivery")] [ProducesResponseType(StatusCodes.Status201Created)] - public async Task PostOnDeliveryAsync([FromBody](string email, int productId) payload) + public async Task PostOnDeliveryAsync([FromBody] (string email, int productId) payload) { var (email, productId) = payload; _logger.LogDebug("Sending on-delivery event"); @@ -72,11 +70,11 @@ public async Task PostOnDeliveryAsync([FromBody](string email, int produ /// /// Post on-received state. /// - /// The event key. + /// The payload. /// [HttpPost("on-received")] [ProducesResponseType(StatusCodes.Status201Created)] - public async Task PostOnReceivedAsync([FromBody](string email, int productId) payload) + public async Task PostOnReceivedAsync([FromBody] (string email, int productId) payload) { var (email, productId) = payload; _logger.LogDebug("Sending on-received event"); diff --git a/Tests/WebSampleS3/Entities/Constants.cs b/Tests/WebSampleS3/Entities/Constants.cs index ff6067b0..0c5c4235 100644 --- a/Tests/WebSampleS3/Entities/Constants.cs +++ b/Tests/WebSampleS3/Entities/Constants.cs @@ -1,22 +1,17 @@ -using Amazon.Runtime.CredentialManagement; +using Amazon; using Amazon.Runtime; +using Amazon.Runtime.CredentialManagement; using Amazon.S3; using EventSourcing.Backbone; -using Amazon; -using System.Net.Sockets; namespace WebSampleS3; -public class Constants +public static class Constants { public const string URI = "hello-event-sourcing"; public const string S3_BUCKET = "event-sourcing-demo"; - //public const string S3_ACCESS_KEY_ENV = "S3_EVENT_DEMO_ACCESS_KEY"; - //public const string S3_SECRET_ENV = "S3_EVENT_DEMO_SECRET"; - //public const string S3_REGION_ENV = "S3_EVENT_DEMO_REGION"; - //public static IAmazonS3 CreateS3Client(RegionEndpoint region, string profile = "playground") public static IAmazonS3 CreateS3Client(RegionEndpoint region, string profile = "playground") { var chain = new CredentialProfileStoreChain(); diff --git a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs index c2d67284..1ffaefb6 100644 --- a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs @@ -1,5 +1,4 @@ using EventSourcing.Backbone; -using WebSampleS3; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 @@ -16,6 +15,7 @@ public static class ConsumerExtensions /// /// The services. /// The URI. + /// The s3 bucket. /// The environment. /// public static IServiceCollection AddShipmentTrackingConsumer diff --git a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs index 99863091..6d3d008b 100644 --- a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs +++ b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs @@ -3,14 +3,10 @@ using EventSourcing.Backbone; -using Microsoft.AspNetCore.Server.Kestrel.Core; - using OpenTelemetry.Exporter; using OpenTelemetry.Resources; using OpenTelemetry.Trace; -using StackExchange.Redis; - using WebSample.Extensions; diff --git a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs index 6c9fa02c..e4f931c5 100644 --- a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs @@ -1,5 +1,4 @@ using EventSourcing.Backbone; -using WebSampleS3; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 @@ -16,6 +15,7 @@ public static class ShipmentTrackingProducerExtensions /// /// The services. /// The URI. + /// The s3 bucket. /// The environment. /// public static IServiceCollection AddShipmentTrackingProducer diff --git a/Tests/WebSampleS3/Jobs/ConsumerJob.cs b/Tests/WebSampleS3/Jobs/ConsumerJob.cs index 5b0564e9..010a9c5a 100644 --- a/Tests/WebSampleS3/Jobs/ConsumerJob.cs +++ b/Tests/WebSampleS3/Jobs/ConsumerJob.cs @@ -1,11 +1,6 @@ -using System.Text.Json; -using System.Threading; - -using EventSourcing.Backbone; +using EventSourcing.Backbone; using EventSourcing.Backbone.Building; -using Microsoft.Extensions.Hosting; - namespace WebSampleS3; /// @@ -31,7 +26,7 @@ public sealed class ConsumerJob : IHostedService, IAsyncDisposable /// The producer. public ConsumerJob( ILogger logger, - IConsumerReadyBuilder consumerBuilder) + IConsumerReadyBuilder consumerBuilder) { _builder = consumerBuilder.WithLogger(logger); _subscriber = new Subscriber(logger); @@ -48,7 +43,7 @@ public ConsumerJob( Task IHostedService.StartAsync(CancellationToken cancellationToken) { _cancellationTokenSource = new CancellationTokenSource(); - var canellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); + var canellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); _subscription = _builder // .Group(CONSUMER_GROUP) .WithCancellation(canellation.Token) @@ -122,7 +117,7 @@ ValueTask IShipmentTrackingConsumer.OrderPlacedAsync(User user, Product product, { // get the current event metadata Metadata? meta = ConsumerMetadata.Context; - + _logger.LogInformation("handling OrderPlaced [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, user.email, product.id, time); return ValueTask.CompletedTask; } @@ -170,7 +165,7 @@ ValueTask IShipmentTrackingConsumer.OnReceivedAsync(string email, int productId, { // get the current event metadata Metadata? meta = ConsumerMetadata.Context; - + _logger.LogInformation("handling OnReceived [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); return ValueTask.CompletedTask; } diff --git a/Tests/WebSampleS3/Program.cs b/Tests/WebSampleS3/Program.cs index fe2518ce..2ec35e56 100644 --- a/Tests/WebSampleS3/Program.cs +++ b/Tests/WebSampleS3/Program.cs @@ -1,8 +1,5 @@ using Amazon.S3; -using StackExchange.Redis; - -using WebSample; using WebSample.Extensions; using WebSampleS3; @@ -20,7 +17,6 @@ string appName = environment.ApplicationName; builder.Services.AddOpenTelemetry(environment, appName); -builder.Services.AddEventSourceRedisConnection(); string URI = "shipment-tracking"; // make sure to create the bucket on AWS S3 with both prefix 'dev.' and 'prod.' and any other environment you're using (like staging,etc.) diff --git a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs index 0412864d..5fc23e8d 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/RoslynHelper.cs @@ -87,11 +87,11 @@ public static IEnumerable GetUsing(this SyntaxNode syntaxNode) } } - if(syntaxNode.Parent == null) - yield break; - + if (syntaxNode.Parent == null) + yield break; + foreach (var u in GetUsing(syntaxNode.Parent)) - { + { yield return u; } } From 04cd83f1eb8974406def1eedd34b1c4733f630ec Mon Sep 17 00:00:00 2001 From: bnayae Date: Mon, 5 Jun 2023 11:54:54 +0000 Subject: [PATCH 103/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 92d958aa..f530f65d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.86 + 1.2.87 1.2.86: Breaking changes: S3Strategy was renamed to S3Storage From 9534e83bcbe92fe31b71431041215574f0f91148 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Mon, 5 Jun 2023 11:55:12 +0000 Subject: [PATCH 104/178] Increment Version --- Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f530f65d..4e22afa5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.2.87 + 1.2.88 - 1.2.86: + 1.2.87: Breaking changes: S3Strategy was renamed to S3Storage @@ -46,7 +46,7 @@ - + From 98b2994a1042f1ab578a5bb2961307665eecf39d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:12:33 +0300 Subject: [PATCH 105/178] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index cd0259bf..741a9d78 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +[![Deploy](https://github.com/bnayae/Event-Sourcing-Backbone/actions/workflows/build-publish-v2.yml/badge.svg)](https://github.com/bnayae/Event-Sourcing-Backbone/actions/workflows/build-publish-v2.yml) + +[![NuGet](https://img.shields.io/nuget/v/EventSourcing.Backbone.SrcGen.svg)](https://www.nuget.org/packages/EventSourcing.Backbone.SrcGen/) +[![NuGet](https://img.shields.io/nuget/v/EventSourcing.Backbone.Abstractions.svg)](https://www.nuget.org/packages/EventSourcing.Backbone.Abstractions/) + # Event-Source-Backbone ## Understanding Event Sourcing From 47f3d57b32d1b54d6b76968a76a35468ca64df7f Mon Sep 17 00:00:00 2001 From: bnayae Date: Mon, 5 Jun 2023 12:12:46 +0000 Subject: [PATCH 106/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 4e22afa5..c6559ab6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.88 + 1.2.89 1.2.87: Breaking changes: S3Strategy was renamed to S3Storage From 9a2008657d7ebaf87d673dce8075e04bd24981c6 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Mon, 5 Jun 2023 12:13:03 +0000 Subject: [PATCH 107/178] Increment Version --- Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index c6559ab6..75b3ea7c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.2.89 + 1.2.90 - 1.2.87: + 1.2.88: Breaking changes: S3Strategy was renamed to S3Storage @@ -46,7 +46,7 @@ - + From 523c8a6a270b320c236dfdacf0cc5e7424e862ed Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:15:57 +0300 Subject: [PATCH 108/178] build: fix --- EventSourcing.Backbone.sln | 1 - Tests/WebSampleS3/Jobs/ConsumerJob.cs | 5 +++-- Tests/WebSampleS3/WebSampleS3.csproj | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 7414dec4..d792a780 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -172,7 +172,6 @@ Global {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Gen|Any CPU.Build.0 = Gen|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/Tests/WebSampleS3/Jobs/ConsumerJob.cs b/Tests/WebSampleS3/Jobs/ConsumerJob.cs index 010a9c5a..e33e567c 100644 --- a/Tests/WebSampleS3/Jobs/ConsumerJob.cs +++ b/Tests/WebSampleS3/Jobs/ConsumerJob.cs @@ -6,7 +6,8 @@ namespace WebSampleS3; /// /// Consumer job /// -/// +/// +/// /// public sealed class ConsumerJob : IHostedService, IAsyncDisposable { @@ -23,7 +24,6 @@ public sealed class ConsumerJob : IHostedService, IAsyncDisposable /// /// The logger. /// The builder. - /// The producer. public ConsumerJob( ILogger logger, IConsumerReadyBuilder consumerBuilder) @@ -90,6 +90,7 @@ async ValueTask IAsyncDisposable.DisposeAsync() /// /// The subscriber implementation /// + /// /// private sealed class Subscriber : IShipmentTrackingConsumer { diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index c7bcd45b..d4b6ac84 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -1,11 +1,11 @@ - net7.0 - enable - enable + Debug;Release;Gen + false + From 9ccea4bb8373bf8189f81fa91cfafcd7316a4b61 Mon Sep 17 00:00:00 2001 From: bnayae Date: Mon, 5 Jun 2023 12:16:25 +0000 Subject: [PATCH 109/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 75b3ea7c..4d6d32c2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.90 + 1.2.91 1.2.88: Breaking changes: S3Strategy was renamed to S3Storage From d8c52632b25dc6a554dda682ea36935b19b815a4 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Mon, 5 Jun 2023 12:16:40 +0000 Subject: [PATCH 110/178] Increment Version --- Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 4d6d32c2..f309859c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.2.91 + 1.2.92 - 1.2.88: + 1.2.89: Breaking changes: S3Strategy was renamed to S3Storage @@ -46,7 +46,7 @@ - + From 70a1105e0649ed31c49c6e001a8a4a603761330d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:41:34 +0300 Subject: [PATCH 111/178] fix: S3Strategy -> S3Storage --- .../S3ConsumerStorageStrategyExtension.cs | 2 +- .../S3ProducerStorageStrategyExtension.cs | 2 +- .../S3StoreCredentialsTests.cs | 4 ++-- .../RegisterEventSourceExtensions.cs | 8 ++++---- Tests/WebSampleS3/Extensions/ConsumerExtensions.cs | 2 +- .../ShipmentTrackingProducerExtensions.cs | 2 +- Tests/WebSampleS3/Jobs/ConsumerJob.cs | 2 -- 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index c6d37843..84d97bdf 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -85,7 +85,7 @@ ValueTask Local(ILogger logger) /// The options. /// Type of the target. /// - public static IConsumerStoreStrategyBuilder ResolveS3Strategy( + public static IConsumerStoreStrategyBuilder ResolveS3Storage( this IConsumerIocStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index d1fb8d46..cb0f48e5 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -87,7 +87,7 @@ ValueTask Local(ILogger logger) /// Type of the target. /// The filter of which keys in the bucket will be store into this storage. /// - public static IProducerStoreStrategyBuilder ResolveS3Strategy( + public static IProducerStoreStrategyBuilder ResolveS3Storage( this IProducerIocStoreStrategyBuilder builder, S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All, diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs index 745f4532..1b94dda9 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs @@ -48,7 +48,7 @@ public S3StoreCredentialsTests(ITestOutputHelper outputHelper) { var logger = ioc.GetService>(); IProducerHooksBuilder producer = ioc.ResolveRedisProducerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) .WithLogger(logger!) .Uri(TEST_URI); @@ -58,7 +58,7 @@ public S3StoreCredentialsTests(ITestOutputHelper outputHelper) { //var logger = ioc.GetService>(); IConsumerReadyBuilder consumer = ioc.ResolveRedisConsumerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) .Uri(TEST_URI); return consumer; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 05bc61df..3d111c8b 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -22,7 +22,7 @@ public static IServiceCollection AddEventSource( services.AddSingleton(ioc => { IRawProducer producer = ioc.ResolveRedisProducerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) .BuildRaw(); return producer; }); @@ -30,7 +30,7 @@ public static IServiceCollection AddEventSource( { ILogger logger = ioc.GetService>() ?? throw new EventSourcingException("Logger is missing"); IEventFlowProducer producer = ioc.ResolveRedisProducerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) .Environment(env) .Uri(URI) .WithLogger(logger) @@ -41,7 +41,7 @@ public static IServiceCollection AddEventSource( { IConsumerReadyBuilder consumer = ioc.ResolveRedisConsumerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) // .AddS3Storage(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { @@ -56,7 +56,7 @@ public static IServiceCollection AddEventSource( { IConsumerHooksBuilder consumer = ioc.ResolveRedisConsumerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) // .AddS3Storage(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { diff --git a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs index 1ffaefb6..2d1fba6e 100644 --- a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs @@ -30,7 +30,7 @@ public static IServiceCollection AddShipmentTrackingConsumer { IConsumerReadyBuilder consumer = ioc.ResolveRedisConsumerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) .WithOptions(o => o with { TraceAsParent = TimeSpan.FromMinutes(10), diff --git a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs index e4f931c5..a15b8bac 100644 --- a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs @@ -30,7 +30,7 @@ public static IServiceCollection AddShipmentTrackingProducer { ILogger logger = ioc.GetService>() ?? throw new EventSourcingException("Logger is missing"); IShipmentTrackingProducer producer = ioc.ResolveRedisProducerChannel() - .ResolveS3Strategy(s3Options) + .ResolveS3Storage(s3Options) .Environment(env) .Uri(uri) .WithLogger(logger) diff --git a/Tests/WebSampleS3/Jobs/ConsumerJob.cs b/Tests/WebSampleS3/Jobs/ConsumerJob.cs index e33e567c..d0de0c1f 100644 --- a/Tests/WebSampleS3/Jobs/ConsumerJob.cs +++ b/Tests/WebSampleS3/Jobs/ConsumerJob.cs @@ -90,8 +90,6 @@ async ValueTask IAsyncDisposable.DisposeAsync() /// /// The subscriber implementation /// - /// - /// private sealed class Subscriber : IShipmentTrackingConsumer { private readonly ILogger _logger; From ac8da05a51e2fd3a7832216c45de01fffa7c00c3 Mon Sep 17 00:00:00 2001 From: bnayae Date: Mon, 5 Jun 2023 12:42:05 +0000 Subject: [PATCH 112/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f309859c..8a6c0c40 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.92 + 1.2.93 1.2.89: Breaking changes: S3Strategy was renamed to S3Storage From 21c0a2927068f5e67f36f993155a961e6cdd7149 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Mon, 5 Jun 2023 12:42:24 +0000 Subject: [PATCH 113/178] Increment Version --- Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8a6c0c40..766b6feb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.2.93 + 1.2.94 - 1.2.89: + 1.2.90: Breaking changes: S3Strategy was renamed to S3Storage @@ -46,7 +46,7 @@ - + From 33cc806db65622b4f0254dfe2c6575137789c586 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 10:36:58 +0300 Subject: [PATCH 114/178] rfc: comment --- EventSourcing.Backbone.sln | 3 +++ .../EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index d792a780..9e864a7b 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -39,7 +39,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.Chan EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6BD7580F-9EAE-4046-8EA9-10FBC8CF2C56}" ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore Directory.Build.props = Directory.Build.props README.md = README.md EndProjectSection diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index bf85fdb8..4390f01c 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -59,7 +59,9 @@ public static IServiceCollection AddOpenTelemetry( string shortAppName, IConnectionMultiplexer redisConnection) { - // see: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + // see: + // https://opentelemetry.io/docs/instrumentation/net/getting-started/ + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables Console.WriteLine($"JAEGER endpoint: key='OTEL_EXPORTER_JAEGER_ENDPOINT', env='{hostEnv.EnvironmentName}'"); // will be visible in the pods logs From 44765e30d813a140f1c696003c40bc807b064cc2 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 6 Jun 2023 07:37:26 +0000 Subject: [PATCH 115/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 766b6feb..1df21c96 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.94 + 1.2.95 1.2.90: Breaking changes: S3Strategy was renamed to S3Storage From 001ab4fd85826ca86bf04a7e2bcd7caed8add695 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 6 Jun 2023 07:37:44 +0000 Subject: [PATCH 116/178] Increment Version --- Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1df21c96..1a7d676d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.2.95 + 1.2.96 - 1.2.90: + 1.2.91: Breaking changes: S3Strategy was renamed to S3Storage @@ -46,7 +46,7 @@ - + From de223e2a5c0b870e6fdf79b76f1d8fd6cea56209 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:07:04 +0300 Subject: [PATCH 117/178] feat: Ack from consumer metadata --- .../Consumer/Ack/IAck.cs | 13 +- .../Consumer/Ack/IAckOperations.cs | 24 + .../Consumer/ConsumerMetadata.cs | 31 +- .../AckPatternsTests.cs | 434 ++++++++++++++++++ .../EndToEndTests.cs | 209 --------- 5 files changed, 488 insertions(+), 223 deletions(-) create mode 100644 EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs create mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAck.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAck.cs index 487b2635..f2d3c4ee 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAck.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAck.cs @@ -5,18 +5,7 @@ /// message from process again by the consumer) ///
/// - public interface IAck : IAsyncDisposable + public interface IAck : IAckOperations, IAsyncDisposable { - /// - /// Preform acknowledge (which should prevent the - /// message from process again by the consumer) - /// - ValueTask AckAsync(); - - /// - /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) - /// - /// - ValueTask CancelAsync(); } } diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs new file mode 100644 index 00000000..d2e45a99 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs @@ -0,0 +1,24 @@ +namespace EventSourcing.Backbone +{ + /// + /// Preform acknowledge (which should prevent the + /// message from process again by the consumer) + /// + /// + public interface IAckOperations + { + /// + /// Preform acknowledge (which should prevent the + /// message from process again by the consumer). + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) + /// + ValueTask AckAsync(); + + /// + /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) + /// + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) + /// + ValueTask CancelAsync(); + } +} diff --git a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs index 4ae94621..c19c346c 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs @@ -8,14 +8,18 @@ namespace EventSourcing.Backbone /// It represent the operation's intent or represent event. ///
[DebuggerDisplay("{Metadata.Uri} [{Metadata.MessageId} at {Metadata.ProducedAt}]")] - public sealed class ConsumerMetadata + public sealed class ConsumerMetadata: IAckOperations { internal static readonly AsyncLocal _metaContext = new AsyncLocal(); /// /// Get the metadata context /// - public static ConsumerMetadata? Context => _metaContext.Value; + public static ConsumerMetadata Context => _metaContext.Value ?? throw new EventSourcingException( + """ + Consumer metadata doesn't available on the current context + (make sure you try to consume it within a scope of a consuming method call)"); + """); #region Ctor @@ -68,5 +72,28 @@ public static implicit operator Metadata(ConsumerMetadata? instance) } #endregion // Cast overloads + + #region AckAsync + + /// + /// Preform acknowledge (which should prevent the + /// message from process again by the consumer) + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing). + /// + public ValueTask AckAsync() => Ack.Current.AckAsync(); + + #endregion // AckAsync + + #region AckAsync + + /// + /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) + /// + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing). + /// + public ValueTask CancelAsync() => Ack.Current.AckAsync(); + + #endregion // AckAsync + } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs new file mode 100644 index 00000000..fdf989a1 --- /dev/null +++ b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs @@ -0,0 +1,434 @@ +using System.Diagnostics; +using System.Threading.Tasks.Dataflow; + +using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Channels.RedisProvider; +using EventSourcing.Backbone.Enums; +using EventSourcing.Backbone.UnitTests.Entities; + +using FakeItEasy; + +using Microsoft.Extensions.Logging; + +using Polly; + +using StackExchange.Redis; + +using Xunit; +using Xunit.Abstractions; + +using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; + +#pragma warning disable S3881 // "IDisposable" should be implemented correctly + +// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest + +namespace EventSourcing.Backbone.Tests +{ + /// + /// The end to end tests. + /// + public class AckPatternsTests : IDisposable + { + private readonly ITestOutputHelper _outputHelper; + private readonly ISequenceOperationsConsumer _subscriber = A.Fake(); + private readonly SequenceOperationsConsumerBridge _subscriberBridge; + private readonly IProducerStoreStrategyBuilder _producerBuilder; + private readonly IConsumerStoreStrategyBuilder _consumerBuilder; + + private readonly string ENV = $"test"; + private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + + private readonly ILogger _fakeLogger = A.Fake(); + private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; + private const int TIMEOUT = 1_000 * 50; + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + /// The output helper. + /// The producer channel builder. + /// The consumer channel builder. + public AckPatternsTests( + ITestOutputHelper outputHelper, + Func? producerChannelBuilder = null, + Func? consumerChannelBuilder = null) + { + _outputHelper = outputHelper; + _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, + configuration: (cfg) => cfg.ServiceName = "mymaster" */); + _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; + var stg = new RedisConsumerChannelSetting + { + DelayWhenEmptyBehavior = new DelayWhenEmptyBehavior + { + CalcNextDelay = ((d, _) => TimeSpan.FromMilliseconds(2)) + } + }; + var consumerBuilder = stg.CreateRedisConsumerBuilder(); + _consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; + + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + .ReturnsLazily(() => + { + Metadata meta = ConsumerMetadata.Context; + if (string.IsNullOrEmpty(meta.EventKey)) + return ValueTask.FromException(new EventSourcingException("Event Key is missing")); + return ValueTask.CompletedTask; + }); + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + .ReturnsLazily(() => Delay()); + A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + .ReturnsLazily(() => Delay()); + + #region A.CallTo(() => _fakeLogger...) + + A.CallTo(() => _fakeLogger.Log( + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored + )) + .Invokes>((level, id, msg, ex, fn) => + _outputHelper.WriteLine( + $"Info: {fn(msg, ex)}")); + + #endregion // A.CallTo(() => _fakeLogger...) + + async ValueTask Delay() => await Task.Delay(200); + + _subscriberBridge = new SequenceOperationsConsumerBridge(_subscriber); + } + + #endregion // Ctor + + #region DefaultOptions + + private ConsumerOptions DefaultOptions( + ConsumerOptions options, + uint? maxMessages = null, + AckBehavior? ackBehavior = null, + PartialConsumerBehavior? behavior = null) + { + var claimTrigger = new ClaimingTrigger { EmptyBatchCount = 5, MinIdleTime = TimeSpan.FromSeconds(3) }; + return options with + { + ClaimingTrigger = claimTrigger, + MaxMessages = maxMessages ?? options.MaxMessages, + AckBehavior = ackBehavior ?? options.AckBehavior, + PartialBehavior = behavior ?? options.PartialBehavior + }; + } + + #endregion // DefaultOptions + + #region OnSucceed_ACK_WithFailure_Test + + [Fact(Timeout = TIMEOUT)] + public async Task OnSucceed_ACK_WithFailure_Test() + { + #region ISequenceOperations producer = ... + + ISequenceOperationsProducer producer = _producerBuilder + .Environment(ENV) + //.WithOptions(producerOption) + .Uri(URI) + .WithLogger(_fakeLogger) + .BuildSequenceOperationsProducer(); + + #endregion // ISequenceOperations producer = ... + + #region A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) + + int tryNumber = 0; + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + .ReturnsLazily(() => + { + // 3 error will be catch by Polly, the 4th one will catch outside of Polly + if (Interlocked.Increment(ref tryNumber) < 5) + throw new ApplicationException("test intensional exception"); + + return ValueTask.CompletedTask; + }); + + #endregion // A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) + + await SendSequenceAsync(producer); + + CancellationToken cancellation = GetCancellationToken(); + + #region await using IConsumerLifetime subscription = ...Subscribe(...) + + await using IConsumerLifetime subscription = _consumerBuilder + .WithOptions(o => DefaultOptions(o, 4, AckBehavior.OnSucceed)) + .WithCancellation(cancellation) + .Environment(ENV) + .Uri(URI) + .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) + .WithLogger(_fakeLogger) + .Group("CONSUMER_GROUP_1") + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") + .Subscribe(_subscriberBridge); + + #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) + + await subscription.Completion; + + #region Validation + + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + .MustHaveHappened( + 3 /* Polly retry */ + 1 /* error */ + 1 /* succeed */, + Times.Exactly); + A.CallTo(() => _subscriber.EarseAsync(4335)) + .MustHaveHappenedOnceExactly(); + + #endregion // Validation + } + + #endregion // OnSucceed_ACK_WithFailure_Test + + #region OnFinaly_ACK_WithFailure_Test + + [Fact(Timeout = TIMEOUT)] + public async Task OnFinaly_ACK_WithFailure_Test() + { + #region ISequenceOperations producer = ... + + ISequenceOperationsProducer producer = _producerBuilder + .Environment(ENV) + .Uri(URI) + .WithLogger(_fakeLogger) + .BuildSequenceOperationsProducer(); + + #endregion // ISequenceOperations producer = ... + + #region A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) + + int tryNumber = 0; + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + .ReturnsLazily(() => + { + // 3 error will be catch by Polly, the 4th one will catch outside of Polly + if (Interlocked.Increment(ref tryNumber) < 5) + throw new ApplicationException("test intensional exception"); + + return ValueTask.CompletedTask; + }); + + #endregion // A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) + + await SendSequenceAsync(producer); + + + CancellationToken cancellation = GetCancellationToken(); + + #region await using IConsumerLifetime subscription = ...Subscribe(...) + + await using IConsumerLifetime subscription = _consumerBuilder + .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnFinally)) + .WithCancellation(cancellation) + .Environment(ENV) + .Uri(URI) + .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) + .WithLogger(_fakeLogger) + .Group("CONSUMER_GROUP_1") + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") + .Subscribe(_subscriberBridge); + + #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) + + await subscription.Completion; + + #region Validation + + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + .MustHaveHappened( + 3 /* Polly retry */ + 1 /* error */ , + Times.Exactly); + A.CallTo(() => _subscriber.EarseAsync(4335)) + .MustHaveHappenedOnceExactly(); + + #endregion // Validation + } + + #endregion // OnFinaly_ACK_WithFailure_Test + + #region Manual_ACK_Test + + [Fact(Timeout = TIMEOUT)] + public async Task Manual_ACK_Test() + { + #region ISequenceOperations producer = ... + + ISequenceOperationsProducer producer = _producerBuilder + .Environment(ENV) + //.WithOptions(producerOption) + .Uri(URI) + .BuildSequenceOperationsProducer(); + + #endregion // ISequenceOperations producer = ... + + #region A.CallTo(...).ReturnsLazily(...) + + int tryNumber = 0; + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + .ReturnsLazily(() => Ack.Current.AckAsync()); + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + .ReturnsLazily(async () => + { + // 3 error will be catch by Polly, the 4th one will catch outside of Polly + if (Interlocked.Increment(ref tryNumber) < 5) + throw new ApplicationException("test intensional exception"); + ConsumerMetadata meta = ConsumerMetadata.Context; + await meta.AckAsync(); + //await Ack.Current.AckAsync(); + }); + A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + .ReturnsLazily(() => Ack.Current.AckAsync()); + + #endregion // A.CallTo(...).ReturnsLazily(...) + + await SendSequenceAsync(producer); + + CancellationToken cancellation = GetCancellationToken(); + + #region await using IConsumerLifetime subscription = ...Subscribe(...) + + await using IConsumerLifetime subscription = _consumerBuilder + .WithOptions(o => DefaultOptions(o, 4, AckBehavior.Manual)) + .WithCancellation(cancellation) + .Environment(ENV) + .Uri(URI) + .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) + .WithLogger(_fakeLogger) + .Group("CONSUMER_GROUP_1") + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") + .Subscribe(_subscriberBridge); + + #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) + + await subscription.Completion; + + #region Validation + + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + .MustHaveHappened( + 3 /* Polly retry */ + 1 /* error */ + 1 /* succeed */, + Times.Exactly); + A.CallTo(() => _subscriber.EarseAsync(4335)) + .MustHaveHappenedOnceExactly(); + + #endregion // Validation + } + + #endregion // Manual_ACK_Test + + #region SendSequenceAsync + + private static async Task SendSequenceAsync(ISequenceOperationsProducer producer, string pass = "1234") + { + EventKey r1 = await producer.RegisterAsync(USER with { Comment = null }); + EventKey r2 = await producer.LoginAsync("admin", pass); + EventKey r3 = await producer.EarseAsync(4335); + return new[] { r1, r2, r3 }; + } + + #endregion // SendSequenceAsync + + #region GetCancellationToken + + /// + /// Gets the cancellation token. + /// + /// + private static CancellationToken GetCancellationToken() + { + return new CancellationTokenSource(Debugger.IsAttached + ? TimeSpan.FromMinutes(10) + : TimeSpan.FromSeconds(10)).Token; + } + + #endregion // GetCancellationToken + + #region Dispose pattern + + + ~AckPatternsTests() + { + Dispose(); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + try + { + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: _fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; + string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; + var server = conn.GetServer(serverName); + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); + IDatabaseAsync db = conn.GetDatabase(); + + var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8604 // Possible null reference argument. + foreach (string key in keys) + { + ab.Post(key); + } +#pragma warning restore CS8604 // Possible null reference argument. +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. + + ab.Complete(); + ab.Completion.Wait(); + + async Task LocalAsync(string k) + { + try + { + await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); + _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); + } + #region Exception Handling + + catch (RedisTimeoutException ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + catch (Exception ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + + #endregion // Exception Handling + } + } + #region Exception Handling + + catch (RedisTimeoutException ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + catch (Exception ex) + { + _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); + } + + #endregion // Exception Handling + } + + #endregion // Dispose pattern + } +} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 67a835a9..b6c95de6 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -1058,215 +1058,6 @@ public async Task GeneratedContract_Test() #endregion // GeneratedContract_Test - #region OnSucceed_ACK_WithFailure_Test - - [Fact(Timeout = TIMEOUT)] - public async Task OnSucceed_ACK_WithFailure_Test() - { - #region ISequenceOperations producer = ... - - ISequenceOperationsProducer producer = _producerBuilder - .Environment(ENV) - //.WithOptions(producerOption) - .Uri(URI) - .WithLogger(_fakeLogger) - .BuildSequenceOperationsProducer(); - - #endregion // ISequenceOperations producer = ... - - #region A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) - - int tryNumber = 0; - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) - .ReturnsLazily(() => - { - // 3 error will be catch by Polly, the 4th one will catch outside of Polly - if (Interlocked.Increment(ref tryNumber) < 5) - throw new ApplicationException("test intensional exception"); - - return ValueTask.CompletedTask; - }); - - #endregion // A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) - - await SendSequenceAsync(producer); - - CancellationToken cancellation = GetCancellationToken(); - - #region await using IConsumerLifetime subscription = ...Subscribe(...) - - await using IConsumerLifetime subscription = _consumerBuilder - .WithOptions(o => DefaultOptions(o, 4, AckBehavior.OnSucceed)) - .WithCancellation(cancellation) - .Environment(ENV) - .Uri(URI) - .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) - .WithLogger(_fakeLogger) - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .Subscribe(_subscriberBridge); - - #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - - await subscription.Completion; - - #region Validation - - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) - .MustHaveHappened( - 3 /* Polly retry */ + 1 /* error */ + 1 /* succeed */, - Times.Exactly); - A.CallTo(() => _subscriber.EarseAsync(4335)) - .MustHaveHappenedOnceExactly(); - - #endregion // Validation - } - - #endregion // OnSucceed_ACK_WithFailure_Test - - #region OnFinaly_ACK_WithFailure_Test - - [Fact(Timeout = TIMEOUT)] - public async Task OnFinaly_ACK_WithFailure_Test() - { - #region ISequenceOperations producer = ... - - ISequenceOperationsProducer producer = _producerBuilder - .Environment(ENV) - .Uri(URI) - .WithLogger(_fakeLogger) - .BuildSequenceOperationsProducer(); - - #endregion // ISequenceOperations producer = ... - - #region A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) - - int tryNumber = 0; - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) - .ReturnsLazily(() => - { - // 3 error will be catch by Polly, the 4th one will catch outside of Polly - if (Interlocked.Increment(ref tryNumber) < 5) - throw new ApplicationException("test intensional exception"); - - return ValueTask.CompletedTask; - }); - - #endregion // A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) - - await SendSequenceAsync(producer); - - - CancellationToken cancellation = GetCancellationToken(); - - #region await using IConsumerLifetime subscription = ...Subscribe(...) - - await using IConsumerLifetime subscription = _consumerBuilder - .WithOptions(o => DefaultOptions(o, 3, AckBehavior.OnFinally)) - .WithCancellation(cancellation) - .Environment(ENV) - .Uri(URI) - .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) - .WithLogger(_fakeLogger) - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .Subscribe(_subscriberBridge); - - #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - - await subscription.Completion; - - #region Validation - - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) - .MustHaveHappened( - 3 /* Polly retry */ + 1 /* error */ , - Times.Exactly); - A.CallTo(() => _subscriber.EarseAsync(4335)) - .MustHaveHappenedOnceExactly(); - - #endregion // Validation - } - - #endregion // OnFinaly_ACK_WithFailure_Test - - #region Manual_ACK_Test - - [Fact(Timeout = TIMEOUT)] - public async Task Manual_ACK_Test() - { - #region ISequenceOperations producer = ... - - ISequenceOperationsProducer producer = _producerBuilder - .Environment(ENV) - //.WithOptions(producerOption) - .Uri(URI) - .BuildSequenceOperationsProducer(); - - #endregion // ISequenceOperations producer = ... - - #region A.CallTo(...).ReturnsLazily(...) - - - int tryNumber = 0; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) - .ReturnsLazily(() => Ack.Current.AckAsync()); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) - .ReturnsLazily(async () => - { - // 3 error will be catch by Polly, the 4th one will catch outside of Polly - if (Interlocked.Increment(ref tryNumber) < 5) - throw new ApplicationException("test intensional exception"); - - await Ack.Current.AckAsync(); - }); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) - .ReturnsLazily(() => Ack.Current.AckAsync()); - - #endregion // A.CallTo(...).ReturnsLazily(...) - - - await SendSequenceAsync(producer); - - CancellationToken cancellation = GetCancellationToken(); - - #region await using IConsumerLifetime subscription = ...Subscribe(...) - - await using IConsumerLifetime subscription = _consumerBuilder - .WithOptions(o => DefaultOptions(o, 4, AckBehavior.Manual)) - .WithCancellation(cancellation) - .Environment(ENV) - .Uri(URI) - .WithResiliencePolicy(Policy.Handle().RetryAsync(3, (ex, i) => _outputHelper.WriteLine($"Retry {i}"))) - .WithLogger(_fakeLogger) - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .Subscribe(_subscriberBridge); - - #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) - - await subscription.Completion; - - #region Validation - - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) - .MustHaveHappened( - 3 /* Polly retry */ + 1 /* error */ + 1 /* succeed */, - Times.Exactly); - A.CallTo(() => _subscriber.EarseAsync(4335)) - .MustHaveHappenedOnceExactly(); - - #endregion // Validation - } - - #endregion // Manual_ACK_Test - #region Resilience_Test [Fact(Timeout = TIMEOUT)] From 12cc222deca4a8d0c3579fde0ea38c8e33eb1db1 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 13:23:23 +0300 Subject: [PATCH 118/178] feat: completion of consumer meta parameter --- .../AckPatternsTests.cs | 38 ++++---- .../EndToEndExplicitTests.cs | 8 +- .../EndToEndStressTests.cs | 6 +- .../EndToEndTests.cs | 92 +++++++++---------- .../HelloWorld/HelloWorldTests.cs | 14 +-- .../InheritanceTests.cs | 30 +++--- .../MigrationTest.cs | 14 +-- .../S3StoreCredentialsTests.cs | 8 +- .../Contracts/ISimpleEvent.cs | 1 - .../EndToEndTests.cs | 30 +++--- .../SequenceOperationsConsumer.cs | 16 ++-- .../StoreStrategyTests.cs | 6 +- .../Subscriptions/SimpleEventSubscription.cs | 4 +- .../SimpleEventSubscriptionBase.cs | 11 ++- .../SimpleEventSubscriptionBridge.cs | 5 +- .../SimpleEventSubscriptionFromGen.cs | 4 +- .../Jobs/MicroDemoJob.cs | 10 +- Tests/HelloWorld/ConsumingEvents/Program.cs | 6 +- Tests/WebSampleS3/Jobs/ConsumerJob.cs | 20 ++-- .../Concrete/BridgeIncrementalGenerator.cs | 12 ++- .../Concrete/ContractIncrementalGenerator.cs | 20 +++- .../Properties/launchSettings.json | 2 +- 22 files changed, 190 insertions(+), 167 deletions(-) diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs index fdf989a1..23b9e5c5 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs @@ -70,7 +70,7 @@ public AckPatternsTests( var consumerBuilder = stg.CreateRedisConsumerBuilder(); _consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => { Metadata meta = ConsumerMetadata.Context; @@ -78,9 +78,9 @@ public AckPatternsTests( return ValueTask.FromException(new EventSourcingException("Event Key is missing")); return ValueTask.CompletedTask; }); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); #region A.CallTo(() => _fakeLogger...) @@ -144,7 +144,7 @@ public async Task OnSucceed_ACK_WithFailure_Test() #region A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) int tryNumber = 0; - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => { // 3 error will be catch by Polly, the 4th one will catch outside of Polly @@ -179,13 +179,13 @@ public async Task OnSucceed_ACK_WithFailure_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappened( 3 /* Polly retry */ + 1 /* error */ + 1 /* succeed */, Times.Exactly); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -211,7 +211,7 @@ public async Task OnFinaly_ACK_WithFailure_Test() #region A.CallTo(() => _subscriber.LoginAsync(throw 1 time)) int tryNumber = 0; - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => { // 3 error will be catch by Polly, the 4th one will catch outside of Polly @@ -247,13 +247,13 @@ public async Task OnFinaly_ACK_WithFailure_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappened( 3 /* Polly retry */ + 1 /* error */ , Times.Exactly); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -279,19 +279,19 @@ public async Task Manual_ACK_Test() #region A.CallTo(...).ReturnsLazily(...) int tryNumber = 0; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Ack.Current.AckAsync()); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) - .ReturnsLazily(async () => + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) + .ReturnsLazily(async (meta, email, pass) => { // 3 error will be catch by Polly, the 4th one will catch outside of Polly if (Interlocked.Increment(ref tryNumber) < 5) throw new ApplicationException("test intensional exception"); - ConsumerMetadata meta = ConsumerMetadata.Context; await meta.AckAsync(); + //ConsumerMetadata meta = ConsumerMetadata.Context; //await Ack.Current.AckAsync(); }); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Ack.Current.AckAsync()); #endregion // A.CallTo(...).ReturnsLazily(...) @@ -319,13 +319,13 @@ public async Task Manual_ACK_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappened( 3 /* Polly retry */ + 1 /* error */ + 1 /* succeed */, Times.Exactly); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 10b7cbe4..6e17e650 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -63,9 +63,9 @@ public EndToEndExplicitTests( _consumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(); _consumerBuilder = consumerChannelBuilder?.Invoke(_consumerBuilder, _fakeLogger) ?? _consumerBuilder; - A.CallTo(() => _subscriber.Stage1Async(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.Stage1Async(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => ValueTask.CompletedTask); - A.CallTo(() => _subscriber.Stage2Async(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.Stage2Async(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); #region A.CallTo(() => _fakeLogger...) @@ -133,9 +133,9 @@ public async Task OnSucceed_ACK_Test() #region Validation - A.CallTo(() => _subscriber.Stage1Async(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.Stage1Async(A.Ignored, A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.Stage2Async(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.Stage2Async(A.Ignored, A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); #endregion // Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 6b53008a..5f26c45c 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -59,11 +59,11 @@ public EndToEndStressTests( var claimTrigger = new ClaimingTrigger { EmptyBatchCount = 5, MinIdleTime = TimeSpan.FromSeconds(3) }; _consumerBuilder = consumerBuilder.WithOptions(o => o with { ClaimingTrigger = claimTrigger }); - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => ValueTask.CompletedTask); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); #region A.CallTo(() => _fakeLogger...) diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index b6c95de6..61166d5c 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -80,7 +80,7 @@ public EndToEndTests( var consumerBuilder = stg.CreateRedisConsumerBuilder(); _consumerBuilder = consumerChannelBuilder?.Invoke(consumerBuilder, _fakeLogger) ?? consumerBuilder; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => { Metadata meta = ConsumerMetadata.Context; @@ -88,9 +88,9 @@ public EndToEndTests( return ValueTask.FromException(new EventSourcingException("Event Key is missing")); return ValueTask.CompletedTask; }); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); #region A.CallTo(() => _fakeLogger...) @@ -173,11 +173,11 @@ public async Task Environmet_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -228,9 +228,9 @@ public async Task PartialConsumer_Strict_Succeed_Test() A.CallTo(_fakeLogger).Where(call => call.Method.Name == "Log" && call.GetArgument(0) == LogLevel.Critical).MustNotHaveHappened(); - A.CallTo(() => _stage1Consumer.Stage1Async(A.Ignored, A.Ignored)) + A.CallTo(() => _stage1Consumer.Stage1Async(A.Ignored, A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _stage2Consumer.Stage2Async(A.Ignored, A.Ignored)) + A.CallTo(() => _stage2Consumer.Stage2Async(A.Ignored, A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -330,7 +330,7 @@ public async Task PartialConsumer_Allow_Test() #region Validation - A.CallTo(() => _stage2Consumer.Stage2Async(A.Ignored, A.Ignored)) + A.CallTo(() => _stage2Consumer.Stage2Async(A.Ignored, A.Ignored, A.Ignored)) .MustHaveHappened((int)times, Times.Exactly); #endregion // Validation @@ -983,11 +983,11 @@ public async Task Until_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -1046,11 +1046,11 @@ public async Task GeneratedContract_Test() #region Validation - A.CallTo(() => _autoSubscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _autoSubscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedTwiceExactly(); - A.CallTo(() => _autoSubscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _autoSubscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedTwiceExactly(); - A.CallTo(() => _autoSubscriber.EarseAsync(4335)) + A.CallTo(() => _autoSubscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedTwiceExactly(); #endregion // Validation @@ -1074,9 +1074,9 @@ public async Task Resilience_Test() #endregion // ISequenceOperations producer = ... int tryNumber = 0; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Ack.Current.AckAsync()); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(async () => { if (Interlocked.Increment(ref tryNumber) == 1) @@ -1084,7 +1084,7 @@ public async Task Resilience_Test() await Ack.Current.AckAsync(); }); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Ack.Current.AckAsync()); @@ -1111,11 +1111,11 @@ public async Task Resilience_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedTwiceExactly(); /* 1 Polly, 1 succeed */ - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -1242,46 +1242,46 @@ await Task.WhenAll( #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberPrefix.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriberPrefix.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberPrefix.LoginAsync("admin", "p0")) + A.CallTo(() => _subscriberPrefix.LoginAsync(A.Ignored, "admin", "p0")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberPrefix.EarseAsync(4335)) + A.CallTo(() => _subscriberPrefix.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberPrefix1.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriberPrefix1.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberPrefix1.LoginAsync("admin", "p1")) + A.CallTo(() => _subscriberPrefix1.LoginAsync(A.Ignored, "admin", "p1")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberPrefix1.EarseAsync(4335)) + A.CallTo(() => _subscriberPrefix1.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberSuffix.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriberSuffix.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberSuffix.LoginAsync("admin", "s0")) + A.CallTo(() => _subscriberSuffix.LoginAsync(A.Ignored, "admin", "s0")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberSuffix.EarseAsync(4335)) + A.CallTo(() => _subscriberSuffix.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberSuffix1.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriberSuffix1.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberSuffix1.LoginAsync("admin", "s1")) + A.CallTo(() => _subscriberSuffix1.LoginAsync(A.Ignored, "admin", "s1")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberSuffix1.EarseAsync(4335)) + A.CallTo(() => _subscriberSuffix1.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberDynamic.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriberDynamic.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberDynamic.LoginAsync("admin", "d")) + A.CallTo(() => _subscriberDynamic.LoginAsync(A.Ignored, "admin", "d")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberDynamic.EarseAsync(4335)) + A.CallTo(() => _subscriberDynamic.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -1309,14 +1309,14 @@ public async Task Claim_Test() #region A.CallTo(...).ReturnsLazily(...) - A.CallTo(() => otherSubscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => otherSubscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => { throw new ApplicationException("test intensional exception"); }); - A.CallTo(() => otherSubscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => otherSubscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => ValueTask.CompletedTask); - A.CallTo(() => otherSubscriber.EarseAsync(A.Ignored)) + A.CallTo(() => otherSubscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => ValueTask.CompletedTask); #endregion // A.CallTo(...).ReturnsLazily(...) @@ -1362,18 +1362,18 @@ public async Task Claim_Test() #region Validation - A.CallTo(() => otherSubscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => otherSubscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappened( (3 /* Polly retry */), Times.OrMore); //.MustHaveHappened( // (3 /* Polly retry */ + 1 /* throw */ ) * 3 /* disconnect after 3 messaged */ , // Times.Exactly); - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); #endregion // Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs index 1a3042f8..e8a99efc 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs @@ -45,7 +45,7 @@ public async Task HelloWorld_Minimal_Test() .Uri(URI) .SubscribeHelloConsumer(_subscriber); - A.CallTo(() => _subscriber.WorldAsync(5)) + A.CallTo(() => _subscriber.WorldAsync(A.Ignored, 5)) .Invokes(() => subscription.DisposeAsync()); await producer.HelloAsync("Hi"); @@ -56,9 +56,9 @@ public async Task HelloWorld_Minimal_Test() #region Validation - A.CallTo(() => _subscriber.HelloAsync("Hi")) + A.CallTo(() => _subscriber.HelloAsync(A.Ignored, "Hi")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.WorldAsync(5)) + A.CallTo(() => _subscriber.WorldAsync(A.Ignored, 5)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -100,9 +100,9 @@ public async Task HelloWorld_Test() #region Validation - A.CallTo(() => _subscriber.HelloAsync("Hi")) + A.CallTo(() => _subscriber.HelloAsync(A.Ignored, "Hi")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.WorldAsync(5)) + A.CallTo(() => _subscriber.WorldAsync(A.Ignored, 5)) .MustHaveHappenedOnceExactly(); #endregion // Validation @@ -144,9 +144,9 @@ public async Task HelloWorld_Direct_Endpoint_Test() #region Validation - A.CallTo(() => _subscriber.HelloAsync("Hi")) + A.CallTo(() => _subscriber.HelloAsync(A.Ignored, "Hi")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.WorldAsync(5)) + A.CallTo(() => _subscriber.WorldAsync(A.Ignored, 5)) .MustHaveHappenedOnceExactly(); #endregion // Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index 6980741b..61d58b0b 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -119,15 +119,15 @@ public async Task Inheritance_PartialConsumer_Strict_Succeed_Test(MultiConsumerB #region Prepare var hash = new ConcurrentDictionary(); - A.CallTo(() => _subscriberA.AAsync(1)) + A.CallTo(() => _subscriberA.AAsync(A.Ignored, 1)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); - A.CallTo(() => _subscriberB.BAsync(A.Ignored)) + A.CallTo(() => _subscriberB.BAsync(A.Ignored, A.Ignored)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); - A.CallTo(() => _subscriberAB.DerivedAsync("Hi")) + A.CallTo(() => _subscriberAB.DerivedAsync(A.Ignored, "Hi")) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); - A.CallTo(() => _subscriberAB.AAsync(1)) + A.CallTo(() => _subscriberAB.AAsync(A.Ignored, 1)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); - A.CallTo(() => _subscriberAB.BAsync(A.Ignored)) + A.CallTo(() => _subscriberAB.BAsync(A.Ignored, A.Ignored)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); }); #endregion // Prepare @@ -159,15 +159,15 @@ public async Task Inheritance_PartialConsumer_Strict_Succeed_Test(MultiConsumerB if (multiConsumerBehavior == MultiConsumerBehavior.All) { Assert.True(hash.All(m => m.Value >= 1)); - A.CallTo(() => _subscriberA.AAsync(1)) + A.CallTo(() => _subscriberA.AAsync(A.Ignored, 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberB.BAsync(A.Ignored)) + A.CallTo(() => _subscriberB.BAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberAB.DerivedAsync("Hi")) + A.CallTo(() => _subscriberAB.DerivedAsync(A.Ignored, "Hi")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberAB.AAsync(1)) + A.CallTo(() => _subscriberAB.AAsync(A.Ignored, 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriberAB.BAsync(A.Ignored)) + A.CallTo(() => _subscriberAB.BAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); } else @@ -205,35 +205,35 @@ public async Task Inheritance_ConsumerCooperation_Succeed_Test() #region Prepare var hash = new ConcurrentDictionary(); - A.CallTo(() => _subscriberA.AAsync(1)) + A.CallTo(() => _subscriberA.AAsync(A.Ignored, 1)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); - A.CallTo(() => _subscriberB.BAsync(A.Ignored)) + A.CallTo(() => _subscriberB.BAsync(A.Ignored, A.Ignored)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); - A.CallTo(() => _subscriberAB.DerivedAsync("Hi")) + A.CallTo(() => _subscriberAB.DerivedAsync(A.Ignored, "Hi")) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); - A.CallTo(() => _subscriberAB.AAsync(1)) + A.CallTo(() => _subscriberAB.AAsync(A.Ignored, 1)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); if (Interlocked.Increment(ref i) == 3) tcs.SetResult(i); }); - A.CallTo(() => _subscriberAB.BAsync(A.Ignored)) + A.CallTo(() => _subscriberAB.BAsync(A.Ignored, A.Ignored)) .Invokes(c => { hash.AddOrUpdate(c.Method.Name, 1, (k, v) => v + 1); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 90bcac62..71a453de 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -59,7 +59,7 @@ public MigrationTest( }; _consumerBuilder = stg.CreateRedisConsumerBuilder(); - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => { Metadata meta = ConsumerMetadata.Context; @@ -67,9 +67,9 @@ public MigrationTest( return ValueTask.FromException(new EventSourcingException("Event Key is missing")); return ValueTask.CompletedTask; }); - A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored)) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); - A.CallTo(() => _subscriber.EarseAsync(A.Ignored)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Delay()); #region A.CallTo(() => _fakeLogger...) @@ -201,14 +201,14 @@ public async Task Migration_Test() #region Validation - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => emptySubscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => emptySubscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustNotHaveHappened(); #endregion // Validation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs index 1b94dda9..93fb3921 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/S3StoreCredentialsTests.cs @@ -92,9 +92,9 @@ public async Task S3_Cred() await consumer.Completion; - A.CallTo(() => _subscriber.Step1Async(1)) + A.CallTo(() => _subscriber.Step1Async(A.Ignored, 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.Step2Async(2)) + A.CallTo(() => _subscriber.Step2Async(A.Ignored, 2)) .MustHaveHappenedOnceExactly(); } @@ -121,9 +121,9 @@ public async Task S3_Cred_No_Env_Test() await consumer.Completion; - A.CallTo(() => _subscriber.Step1Async(1)) + A.CallTo(() => _subscriber.Step1Async(A.Ignored, 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.Step2Async(2)) + A.CallTo(() => _subscriber.Step2Async(A.Ignored, 2)) .MustHaveHappenedOnceExactly(); } diff --git a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs index cf9641b0..0fddd083 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs @@ -9,7 +9,6 @@ namespace EventSourcing.Backbone.UnitTests.Entities [EventsContract(EventsContractType.Producer)] [EventsContract(EventsContractType.Consumer)] [Obsolete("This interface is base for code generation, please use ISimpleEventProducer or ISimpleEventConsumer", true)] - [EditorBrowsable(EditorBrowsableState.Never)] public interface ISimpleEvent { /// diff --git a/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs index 35400014..3d22ebac 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs @@ -84,11 +84,11 @@ public async Task End2End_CustomBaseSubscription_Test() await subscription.DisposeAsync(); await ch.Reader.Completion; - A.CallTo(() => _simpleEventConsumer.ExecuteAsync("Id", 1)) + A.CallTo(() => _simpleEventConsumer.ExecuteAsync(A.Ignored, "Id", 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(1, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 1, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(2, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 2, A.Ignored)) .MustHaveHappenedOnceExactly(); } @@ -125,11 +125,11 @@ public async Task End2End_CustomSubscriptionBridge_Test() await subscription.DisposeAsync(); await ch.Reader.Completion; - A.CallTo(() => _simpleEventConsumer.ExecuteAsync("Id", 1)) + A.CallTo(() => _simpleEventConsumer.ExecuteAsync(A.Ignored,"Id", 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(1, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 1, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(2, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 2, A.Ignored)) .MustHaveHappenedOnceExactly(); } @@ -166,11 +166,11 @@ public async Task End2End_GenBaseSubscription_Test() await subscription.DisposeAsync(); await ch.Reader.Completion; - A.CallTo(() => _simpleEventConsumer.ExecuteAsync("Id", 1)) + A.CallTo(() => _simpleEventConsumer.ExecuteAsync(A.Ignored, "Id", 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(1, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 1, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(2, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 2, A.Ignored)) .MustHaveHappenedOnceExactly(); } @@ -207,11 +207,11 @@ public async Task End2End_GenSubscriptionBridge_Test() await subscription.DisposeAsync(); await ch.Reader.Completion; - A.CallTo(() => _simpleEventConsumer.ExecuteAsync("Id", 1)) + A.CallTo(() => _simpleEventConsumer.ExecuteAsync(A.Ignored, "Id", 1)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(1, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 1, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _simpleEventConsumer.RunAsync(2, A.Ignored)) + A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 2, A.Ignored)) .MustHaveHappenedOnceExactly(); } @@ -248,11 +248,11 @@ public async Task End2End_Test() await subscription.DisposeAsync(); await ch.Reader.Completion; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); } diff --git a/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs index 855c22e8..4eeb5368 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Factory/SequenceOperations/SequenceOperationsConsumer.cs @@ -7,19 +7,19 @@ public class SequenceOperationsConsumer : ISequenceOperationsConsumer private readonly ActionBlock _block = new ActionBlock( u => Console.WriteLine(u.Details.Id), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 10 }); - public ValueTask RegisterAsync(User user) + public ValueTask RegisterAsync(ConsumerMetadata meta, User user) { //var msg = new Ackable(user, ack); //_block.Post(msg); return ValueTask.CompletedTask; } - public ValueTask UpdateAsync(User user) => throw new NotImplementedException(); - public ValueTask LoginAsync(string email, string password) => throw new NotImplementedException(); - public ValueTask LogoffAsync(int id) => throw new NotImplementedException(); - public ValueTask ApproveAsync(int id) => throw new NotImplementedException(); - public ValueTask SuspendAsync(int id) => throw new NotImplementedException(); - public ValueTask ActivateAsync(int id) => throw new NotImplementedException(); - public ValueTask EarseAsync(int id) => throw new NotImplementedException(); + public ValueTask UpdateAsync(ConsumerMetadata meta, User user) => throw new NotImplementedException(); + public ValueTask LoginAsync(ConsumerMetadata meta, string email, string password) => throw new NotImplementedException(); + public ValueTask LogoffAsync(ConsumerMetadata meta, int id) => throw new NotImplementedException(); + public ValueTask ApproveAsync(ConsumerMetadata meta, int id) => throw new NotImplementedException(); + public ValueTask SuspendAsync(ConsumerMetadata meta, int id) => throw new NotImplementedException(); + public ValueTask ActivateAsync(ConsumerMetadata meta, int id) => throw new NotImplementedException(); + public ValueTask EarseAsync(ConsumerMetadata meta, int id) => throw new NotImplementedException(); } } diff --git a/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs b/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs index ce1ef435..babedd0e 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/StoreStrategyTests.cs @@ -85,11 +85,11 @@ public async Task Build_Serializer_Producer_Test() await subscription.DisposeAsync(); await _ch.Reader.Completion; - A.CallTo(() => _subscriber.RegisterAsync(A.Ignored)) + A.CallTo(() => _subscriber.RegisterAsync(A.Ignored, A.Ignored)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.LoginAsync("admin", "1234")) + A.CallTo(() => _subscriber.LoginAsync(A.Ignored, "admin", "1234")) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _subscriber.EarseAsync(4335)) + A.CallTo(() => _subscriber.EarseAsync(A.Ignored, 4335)) .MustHaveHappenedOnceExactly(); A.CallTo(() => _producerStorageStrategyA.SaveBucketAsync( A.Ignored, diff --git a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs index 92b7649f..405b93f5 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscription.cs @@ -20,8 +20,8 @@ public SimpleEventSubscription(ISimpleEventConsumer target) #endregion // Ctor - protected override ValueTask ExecuteAsync(string key, int value) => _target.ExecuteAsync(key, value); + protected override ValueTask ExecuteAsync(ConsumerMetadata consumerMetadata, string key, int value) => _target.ExecuteAsync(consumerMetadata, key, value); - protected override ValueTask RunAsync(int id, DateTime date) => _target.RunAsync(id, date); + protected override ValueTask RunAsync(ConsumerMetadata consumerMetadata, int id, DateTime date) => _target.RunAsync(consumerMetadata, id, date); } } diff --git a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs index facdcff8..eed5eeaa 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBase.cs @@ -12,20 +12,21 @@ public abstract class SimpleEventSubscriptionBase : ISubscriptionBridge async Task ISubscriptionBridge.BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) { + ConsumerMetadata consumerMetadata = ConsumerMetadata.Context; switch (announcement.Metadata.Operation) { case nameof(ISimpleEventConsumer.ExecuteAsync): { var p0 = await consumerBridge.GetParameterAsync(announcement, "key"); var p1 = await consumerBridge.GetParameterAsync(announcement, "value"); - await ExecuteAsync(p0, p1); + await ExecuteAsync(consumerMetadata, p0, p1); return true; } case nameof(ISimpleEventConsumer.RunAsync): { var p0 = await consumerBridge.GetParameterAsync(announcement, "id"); var p1 = await consumerBridge.GetParameterAsync(announcement, "date"); - await RunAsync(p0, p1); + await RunAsync(consumerMetadata, p0, p1); return true; } default: @@ -37,17 +38,19 @@ async Task ISubscriptionBridge.BridgeAsync(Announcement announcement, ICon /// /// Executes the asynchronous. /// + /// The consumer metadata. /// The key. /// The value. /// - protected abstract ValueTask ExecuteAsync(string key, int value); + protected abstract ValueTask ExecuteAsync(ConsumerMetadata consumerMetadata, string key, int value); /// /// Runs the asynchronous. /// + /// The consumer metadata. /// The identifier. /// The date. /// - protected abstract ValueTask RunAsync(int id, DateTime date); + protected abstract ValueTask RunAsync(ConsumerMetadata consumerMetadata, int id, DateTime date); } } diff --git a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs index 78ee946c..f9a5bf74 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionBridge.cs @@ -26,20 +26,21 @@ public SimpleEventSubscriptionBridge(ISimpleEventConsumer target) async Task ISubscriptionBridge.BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) { + var meta = ConsumerMetadata.Context; switch (announcement.Metadata.Operation) { case nameof(ISimpleEventConsumer.ExecuteAsync): { var p0 = await consumerBridge.GetParameterAsync(announcement, "key"); var p1 = await consumerBridge.GetParameterAsync(announcement, "value"); - await _target.ExecuteAsync(p0, p1); + await _target.ExecuteAsync(meta, p0, p1); return true; } case nameof(ISimpleEventConsumer.RunAsync): { var p0 = await consumerBridge.GetParameterAsync(announcement, "id"); var p1 = await consumerBridge.GetParameterAsync(announcement, "date"); - await _target.RunAsync(p0, p1); + await _target.RunAsync(meta, p0, p1); return true; } default: diff --git a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs index 8591c7cc..014caa68 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Subscriptions/SimpleEventSubscriptionFromGen.cs @@ -21,8 +21,8 @@ public SimpleEventSubscriptionFromGen(ISimpleEventConsumer target) #endregion // Ctor - protected override ValueTask ExecuteAsync(string key, int value) => _target.ExecuteAsync(key, value); + protected override ValueTask ExecuteAsync(ConsumerMetadata consumerMetadata, string key, int value) => _target.ExecuteAsync(consumerMetadata, key, value); - protected override ValueTask RunAsync(int id, DateTime date) => _target.RunAsync(id, date); + protected override ValueTask RunAsync(ConsumerMetadata consumerMetadata, int id, DateTime date) => _target.RunAsync(consumerMetadata, id, date); } } diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs index 798e4ab4..d85adb5e 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MicroDemoJob.cs @@ -79,9 +79,9 @@ public Subscriber( _producer = producer; } - async ValueTask IEventFlowConsumer.Stage1Async(Person PII, string payload) + async ValueTask IEventFlowConsumer.Stage1Async(ConsumerMetadata consumerMeta, Person PII, string payload) { - Metadata? meta = ConsumerMetadata.Context; + Metadata meta = consumerMeta.Metadata; _logger.LogInformation("Consume First Stage {uri} {PII} {data}", meta?.Uri, PII, payload); @@ -91,11 +91,11 @@ await _producer.Stage2Async( JsonDocument.Parse("{\"data\":10}").RootElement); } - ValueTask IEventFlowConsumer.Stage2Async(JsonElement PII, JsonElement data) + ValueTask IEventFlowConsumer.Stage2Async(ConsumerMetadata consumerMeta, JsonElement PII, JsonElement data) { - var meta = ConsumerMetadata.Context; + Metadata meta = consumerMeta.Metadata; _logger.LogInformation("Consume 2 Stage {uri} {PII} {data}", - meta?.Metadata?.Uri, PII, data); + meta?.Uri, PII, data); return ValueTask.CompletedTask; } diff --git a/Tests/HelloWorld/ConsumingEvents/Program.cs b/Tests/HelloWorld/ConsumingEvents/Program.cs index f1310570..b3f823d2 100644 --- a/Tests/HelloWorld/ConsumingEvents/Program.cs +++ b/Tests/HelloWorld/ConsumingEvents/Program.cs @@ -13,7 +13,7 @@ class Subscription : IHelloEventsConsumer { public static readonly Subscription Instance = new Subscription(); - public ValueTask NameAsync(string name) + public ValueTask NameAsync(ConsumerMetadata meta,string name) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(); @@ -21,13 +21,13 @@ public ValueTask NameAsync(string name) return ValueTask.CompletedTask; } - public ValueTask ColorAsync(ConsoleColor color) + public ValueTask ColorAsync(ConsumerMetadata meta, ConsoleColor color) { Console.ForegroundColor = color; return ValueTask.CompletedTask; } - public ValueTask StarAsync() + public ValueTask StarAsync(ConsumerMetadata meta) { Console.Write("✱"); return ValueTask.CompletedTask; diff --git a/Tests/WebSampleS3/Jobs/ConsumerJob.cs b/Tests/WebSampleS3/Jobs/ConsumerJob.cs index d0de0c1f..c93a9ca1 100644 --- a/Tests/WebSampleS3/Jobs/ConsumerJob.cs +++ b/Tests/WebSampleS3/Jobs/ConsumerJob.cs @@ -108,14 +108,15 @@ public Subscriber( /// /// Handle [OrderPlaced] event. /// + /// The consumer meta. /// The user. /// The product. /// The time. /// - ValueTask IShipmentTrackingConsumer.OrderPlacedAsync(User user, Product product, DateTimeOffset time) + ValueTask IShipmentTrackingConsumer.OrderPlacedAsync(ConsumerMetadata consumerMeta, User user, Product product, DateTimeOffset time) { // get the current event metadata - Metadata? meta = ConsumerMetadata.Context; + Metadata meta = consumerMeta; _logger.LogInformation("handling OrderPlaced [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, user.email, product.id, time); return ValueTask.CompletedTask; @@ -124,14 +125,15 @@ ValueTask IShipmentTrackingConsumer.OrderPlacedAsync(User user, Product product, /// /// Handle [Packings] event. /// + /// The consumer meta. /// The email. /// The product identifier. /// The time. /// - ValueTask IShipmentTrackingConsumer.PackingAsync(string email, int productId, DateTimeOffset time) + ValueTask IShipmentTrackingConsumer.PackingAsync(ConsumerMetadata consumerMeta, string email, int productId, DateTimeOffset time) { // get the current event metadata - Metadata? meta = ConsumerMetadata.Context; + Metadata meta = consumerMeta; _logger.LogInformation("handling Packing [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); return ValueTask.CompletedTask; @@ -140,14 +142,15 @@ ValueTask IShipmentTrackingConsumer.PackingAsync(string email, int productId, Da /// /// Handle [on-delivery] event. /// + /// The consumer meta. /// The email. /// The product identifier. /// The time. /// - ValueTask IShipmentTrackingConsumer.OnDeliveryAsync(string email, int productId, DateTimeOffset time) + ValueTask IShipmentTrackingConsumer.OnDeliveryAsync(ConsumerMetadata consumerMeta, string email, int productId, DateTimeOffset time) { // get the current event metadata - Metadata? meta = ConsumerMetadata.Context; + Metadata meta = consumerMeta; _logger.LogInformation("handling OnDelivery [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); return ValueTask.CompletedTask; @@ -156,14 +159,15 @@ ValueTask IShipmentTrackingConsumer.OnDeliveryAsync(string email, int productId, /// /// Handle [on-received] event. /// + /// The consumer metadata. /// The email. /// The product identifier. /// The time. /// - ValueTask IShipmentTrackingConsumer.OnReceivedAsync(string email, int productId, DateTimeOffset time) + ValueTask IShipmentTrackingConsumer.OnReceivedAsync(ConsumerMetadata consumerMeta, string email, int productId, DateTimeOffset time) { // get the current event metadata - Metadata? meta = ConsumerMetadata.Context; + Metadata meta = consumerMeta; _logger.LogInformation("handling OnReceived [{message-id}]: email: {email}, product: {productId}, which produce at {time}", meta.MessageId, email, productId, time); return ValueTask.CompletedTask; diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index bf13fe5b..b583f39a 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -189,6 +189,7 @@ protected GenInstruction OnGenerateConsumerBridge( builder.AppendLine("\t\t{"); if (allMethods.Length != 0) { + builder.AppendLine("\t\t\tConsumerMetadata consumerMetadata = ConsumerMetadata.Context;"); builder.AppendLine("\t\t\tswitch (announcement.Metadata.Operation)"); builder.AppendLine("\t\t\t{"); foreach (var method in allMethods) @@ -207,8 +208,8 @@ protected GenInstruction OnGenerateConsumerBridge( i++; } IEnumerable ps = Enumerable.Range(0, prms.Length).Select(m => $"p{m}"); - - builder.AppendLine($"\t\t\t\t\tvar tasks = _targets.Select(async target => await target.{mtdName}({string.Join(", ", ps)}));"); + string metaParam = ps.Any() ? "consumerMetadata, " : "consumerMetadata"; + builder.AppendLine($"\t\t\t\t\tvar tasks = _targets.Select(async target => await target.{mtdName}({metaParam}{string.Join(", ", ps)}));"); builder.AppendLine("\t\t\t\t\tawait Task.WhenAll(tasks);"); builder.AppendLine("\t\t\t\t\treturn true;"); builder.AppendLine("\t\t\t\t}"); @@ -257,6 +258,7 @@ protected GenInstruction OnGenerateConsumerBase( builder.AppendLine("\t\t\t{"); if (allMethods.Length != 0) { + builder.AppendLine("\t\t\t\tConsumerMetadata consumerMetadata = ConsumerMetadata.Context;"); builder.AppendLine("\t\t\t\tswitch (announcement.Metadata.Operation)"); builder.AppendLine("\t\t\t\t{"); foreach (var method in allMethods) @@ -274,8 +276,9 @@ protected GenInstruction OnGenerateConsumerBase( builder.AppendLine($"\t\t\t\t\t\tvar p{i} = await consumerBridge.GetParameterAsync<{p.Type}>(announcement, \"{pName}\");"); i++; } + string metaParam = prms.Any() ? "consumerMetadata, " : "consumerMetadata"; IEnumerable ps = Enumerable.Range(0, prms.Length).Select(m => $"p{m}"); - builder.AppendLine($"\t\t\t\t\t\tawait {mtdName}({string.Join(", ", ps)});"); + builder.AppendLine($"\t\t\t\t\t\tawait {mtdName}({metaParam}{string.Join(", ", ps)});"); builder.AppendLine("\t\t\t\t\t\treturn true;"); builder.AppendLine("\t\t\t\t\t}"); } @@ -293,7 +296,8 @@ protected GenInstruction OnGenerateConsumerBase( var prms = method.Parameters; IEnumerable ps = prms.Select(p => $"{p.Type} {p.Name}"); - builder.AppendLine($"\t\t\tprotected abstract ValueTask {mtdName}({string.Join(", ", ps)});"); + string metaParam = ps.Any() ? "ConsumerMetadata consumerMetadata, " : "ConsumerMetadata consumerMetadata"; + builder.AppendLine($"\t\t\tprotected abstract ValueTask {mtdName}({metaParam}{string.Join(", ", ps)});"); builder.AppendLine(); } builder.AppendLine("\t\t}"); diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 09927358..7c1c1c75 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -59,16 +59,28 @@ protected override GenInstruction[] OnGenerate( if (method is MethodDeclarationSyntax mds) { CopyDocumentation(builder, kind, mds); + var ps = mds.ParameterList.Parameters.Select(GetParameter); + if (ps.Any()) + builder.AppendLine("\t\t/// The consumer metadata."); builder.Append("\t\tValueTask"); if (isProducer) builder.Append(""); - builder.Append($" {mds.ToNameConvention()}("); - - var ps = mds.ParameterList.Parameters.Select(GetParameter); + builder.AppendLine($" {mds.ToNameConvention()}("); + + if (!isProducer) + { + builder.Append("\t\t\t"); + builder.Append("ConsumerMetadata meta"); + if (ps.Any()) + builder.Append(','); + builder.AppendLine(); + } builder.Append("\t\t\t"); - builder.Append(string.Join(", ", ps)); +#pragma warning disable RS1035 // Do not use APIs banned for analyzers + builder.Append(string.Join(",", ps)); +#pragma warning restore RS1035 // Do not use APIs banned for analyzers builder.AppendLine(");"); builder.AppendLine(); } diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json b/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json index a5034e3a..b818d10a 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json +++ b/src-gen/EventSourcing.Backbone.SrcGen/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { ".EventSourcing.Backbone.SrcGen": { "commandName": "DebugRoslynComponent", - "targetProject": "..\\PlayGroundAbstraction\\PlayGroundAbstraction.csproj" + "targetProject": "..\\EventSourcing.Backbone.SrcGen.Playground\\EventSourcing.Backbone.SrcGen.Playground.csproj" } } } \ No newline at end of file From 6eb55758dd782a92926042648375d71297b2d76f Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 13:36:11 +0300 Subject: [PATCH 119/178] doc: releaseNote --- Directory.Build.props | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 766b6feb..2802c424 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,10 @@ 1.2.94 - 1.2.90: + #1.2.85: Breaking changes: S3Strategy was renamed to S3Storage + #1.2.96 + Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata From e89d69f7127392b7ffe76c883f66790eea8d54a4 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 6 Jun 2023 10:46:59 +0000 Subject: [PATCH 120/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 64609519..9f768773 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.96 + 1.2.97 #1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 3a89aef6c02a87019aa2a05a13b1490d8ba58cc4 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 6 Jun 2023 10:47:16 +0000 Subject: [PATCH 121/178] Increment Version --- Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9f768773..1d3229eb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,10 @@ - 1.2.97 + 1.2.98 - #1.2.85: + #1.2.86: Breaking changes: S3Strategy was renamed to S3Storage - #1.2.96 + #1.2.97 Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata @@ -48,7 +48,7 @@ - + From 9569d14e7cc88f6c99371cd099cdbd10ecf25e9f Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:43:29 +0300 Subject: [PATCH 122/178] rfc: clean up --- ...bone.Channels.RedisProducerProvider.csproj | 2 +- ...one.Channels.S3StoreProvider.Common.csproj | 2 +- ...EventSourcing.Backbone.Abstractions.csproj | 2 +- ...EventSourcing.Backbone.WebEventTest.csproj | 12 +++++----- Tests/WebSampleS3/WebSampleS3.csproj | 14 +++++------ .../Concrete/ContractIncrementalGenerator.cs | 24 +++++++++++-------- 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj index 772d4b76..e2689aab 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/EventSourcing.Backbone.Channels.RedisProducerProvider.csproj @@ -23,7 +23,7 @@ - + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index cf2fa251..92626437 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index 173ca42a..68bd4171 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -20,7 +20,7 @@ - + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index 53d8527a..f3058917 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -12,12 +12,12 @@ - - - - - - + + + + + + diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index d4b6ac84..a1e09d4f 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -19,7 +19,7 @@ - + @@ -31,12 +31,12 @@ - - - - - - + + + + + + diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 7c1c1c75..8cc17861 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -36,16 +36,15 @@ protected override GenInstruction[] OnGenerate( SyntaxReceiverResult info, string[] usingStatements) { - +#pragma warning disable S1481 // Unused local variables should be removed var (type, att, symbol, kind, ns, isProducer, @using) = info; +#pragma warning restore S1481 // Unused local variables should be removed string interfaceName = info.FormatName(); var builder = new StringBuilder(); CopyDocumentation(builder, kind, type, "\t"); var asm = GetType().Assembly.GetName(); builder.AppendLine($"\t[GeneratedCode(\"{asm.Name}\",\"{asm.Version}\")]"); builder.Append($"\tpublic interface {interfaceName}"); - // TODO: [bnaya 2023-05-23] make sure to generate the base interfaces - // TODO: [bnaya 2023-05-23] SellectMany into interface hierarchic var baseTypes = symbol.Interfaces.Select(m => info.FormatName(m.Name)); string inheritance = string.Join(", ", baseTypes); if (string.IsNullOrEmpty(inheritance)) @@ -58,12 +57,18 @@ protected override GenInstruction[] OnGenerate( { if (method is MethodDeclarationSyntax mds) { - CopyDocumentation(builder, kind, mds); + var sb = new StringBuilder(); + CopyDocumentation(sb, kind, mds); var ps = mds.ParameterList.Parameters.Select(GetParameter); - if (ps.Any()) - builder.AppendLine("\t\t/// The consumer metadata."); - - + if (sb.Length != 0 && !isProducer && ps.Any()) + { + string summaryEnds = "/// "; + int idxRet = sb.ToString().IndexOf(summaryEnds); + if(idxRet != -1) + sb.Insert(idxRet + summaryEnds.Length, "\r\n\t\t/// The consumer metadata."); + } + builder.Append(sb); + builder.Append("\t\tValueTask"); if (isProducer) builder.Append(""); @@ -72,10 +77,9 @@ protected override GenInstruction[] OnGenerate( if (!isProducer) { builder.Append("\t\t\t"); - builder.Append("ConsumerMetadata meta"); + builder.Append("ConsumerMetadata consumerMetadata"); if (ps.Any()) builder.Append(','); - builder.AppendLine(); } builder.Append("\t\t\t"); #pragma warning disable RS1035 // Do not use APIs banned for analyzers From 55e276e2bb8642f41dfab737e02ba9c47f1cebd9 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 6 Jun 2023 11:45:52 +0000 Subject: [PATCH 123/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 1d3229eb..02abf36e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.98 + 1.2.99 #1.2.86: Breaking changes: S3Strategy was renamed to S3Storage From 0aad52a7778903ac9438d655d1e176c6994638b9 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 6 Jun 2023 11:46:13 +0000 Subject: [PATCH 124/178] Increment Version --- Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 02abf36e..66b2a9a4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,10 @@ - 1.2.99 + 1.2.100 - #1.2.86: + #1.2.87: Breaking changes: S3Strategy was renamed to S3Storage - #1.2.97 + #1.2.98 Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata @@ -48,7 +48,7 @@ - + From 104d3c8807701fb018de75f56313f88233e5c8e7 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:51:33 +0300 Subject: [PATCH 125/178] fix: release note --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 66b2a9a4..05d55af6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,9 +2,9 @@ 1.2.100 - #1.2.87: + #1.2.85: Breaking changes: S3Strategy was renamed to S3Storage - #1.2.98 + #1.2.96 Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata From f672f4bab9de20449dc36309d7acb7199028dc8a Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 6 Jun 2023 11:51:55 +0000 Subject: [PATCH 126/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 05d55af6..7b3ab7e9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.100 + 1.2.101 #1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 643ae05f9335b14a184129f440556dc0dde427d8 Mon Sep 17 00:00:00 2001 From: CI/CD Date: Tue, 6 Jun 2023 11:52:11 +0000 Subject: [PATCH 127/178] Increment Version --- Directory.Build.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7b3ab7e9..5d2cb966 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,10 @@ - 1.2.101 + 1.2.102 - #1.2.85: + #1.2.86: Breaking changes: S3Strategy was renamed to S3Storage - #1.2.96 + #1.2.97 Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata @@ -48,7 +48,7 @@ - + From 62cbfbbd5a8f98f6cfdd8c6ad072c4d3a3d9c1db Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:56:07 +0300 Subject: [PATCH 128/178] ci: fix version increment --- .github/workflows/build-publish-v2.yml | 25 +------------------------ Directory.Build.props | 4 ++-- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-publish-v2.yml b/.github/workflows/build-publish-v2.yml index 90f250b3..0f0642de 100644 --- a/.github/workflows/build-publish-v2.yml +++ b/.github/workflows/build-publish-v2.yml @@ -26,18 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Increment Version - run: | - perl -pi -e 's/([0-9]+)\.([0-9]+)\.([0-9]+)/"$1.$2.${\( $3+1 )}"/eg' Directory.Build.props - - name: Commit changes - uses: EndBug/add-and-commit@v7 - with: - author_name: CI/CD - author_email: ci.bnaya@gmail.com - message: 'Increment Version' - add: 'Directory.Build.props' + - uses: actions/checkout@v2 - name: Setup .NET uses: actions/setup-dotnet@v1 @@ -68,18 +57,6 @@ jobs: with: dotnet-version: ${{ env.DOTNET_VER }} include-prerelease: ${{ env.INCLUDE_PRERELEASE }} - - #- name: Increment Version - # run: | - # perl -pi -e 's/([0-9]+)\.([0-9]+)\.([0-9]+)/"$1.$2.${\( $3+1 )}"/eg' Directory.Build.props - # shell: bash - #- name: Commit changes - # uses: EndBug/add-and-commit@v7 - # with: - # author_name: CI/CD - # author_email: ${{ inputs.author-email }} - # message: "Increment Version" - # add: "Directory.Build.props" - name: Restore dependencies run: dotnet restore /property:Configuration=Gen diff --git a/Directory.Build.props b/Directory.Build.props index 5d2cb966..5bf00db8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,9 +2,9 @@ 1.2.102 - #1.2.86: + # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage - #1.2.97 + # 1.2.96 Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata From 10989849304b0921be4cc4fdadf1afc32fe945dd Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 6 Jun 2023 11:56:28 +0000 Subject: [PATCH 129/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5bf00db8..86d869d1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.102 + 1.2.103 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From d930b4290b8fca4852b8a597402fb56defd1ab77 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 7 Jun 2023 16:33:44 +0300 Subject: [PATCH 130/178] feat: generate modifiers --- Directory.Build.props | 5 ---- Event-Sourcing-Templates.csproj | 26 +++++++++++++++++++ .../ISample.cs | 3 ++- .../Concrete/ContractIncrementalGenerator.cs | 8 +++++- 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 Event-Sourcing-Templates.csproj diff --git a/Directory.Build.props b/Directory.Build.props index 5bf00db8..23d4662c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -37,11 +37,6 @@ True - - - - - true $(NoWarn);1591 diff --git a/Event-Sourcing-Templates.csproj b/Event-Sourcing-Templates.csproj new file mode 100644 index 00000000..4b3ec16b --- /dev/null +++ b/Event-Sourcing-Templates.csproj @@ -0,0 +1,26 @@ + + + + Template + 1.0.0 + Bnaya.Event-Sourcing.Templates + AdatumCorporation Templates + Me + Templates to use when creating an application for Adatum Corporation. + dotnet-new;templates;contoso + + netstandard2.0 + + true + false + content + $(NoWarn);NU5128 + true + + + + + + + + \ No newline at end of file diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs index 36122209..3f9724e9 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs @@ -12,7 +12,8 @@ public interface ISample ///
/// The i. /// The s. + /// The notes. /// - ValueTask ExecAsync(int i, string s); + ValueTask ExecAsync(int i, string s, params string[] notes); } } diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 8cc17861..7f32bd04 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -99,7 +99,13 @@ protected override GenInstruction[] OnGenerate( return new[] { new GenInstruction(interfaceName, builder.ToString()) }; - string GetParameter(ParameterSyntax p) => $"\r\n\t\t\t{p.Type} {p.Identifier.ValueText}"; + string GetParameter(ParameterSyntax p) + { + var mod = p.Modifiers.FirstOrDefault(); + string modifier = mod == null ? string.Empty : $" {mod} "; + var result = $"\r\n\t\t\t{modifier}{p.Type} {p.Identifier.ValueText}"; + return result; + } } } } From 6fe862aa6b6098e543ab41ccc9effcb22a6383ae Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 7 Jun 2023 16:35:38 +0300 Subject: [PATCH 131/178] feat: meta Env --- .../Entities/Announcement/Metadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs index c707a3d4..2ecd9f97 100644 --- a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs @@ -59,7 +59,7 @@ public record Metadata /// /// Gets the origin environment of the message. /// - public string Environment { get; init; } = string.Empty; + public Env Environment { get; init; } = string.Empty; #endregion Environment From a05bb4263c5a2c9cf91b5e8cf46c3fa52c1d6488 Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 7 Jun 2023 13:35:59 +0000 Subject: [PATCH 132/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2b074b02..e4aa2ad5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.103 + 1.2.104 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 021d908d21213f10790adaf2ad46b69c15493765 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 7 Jun 2023 21:43:11 +0300 Subject: [PATCH 133/178] fix: env equality and serialization --- EventSourcing.Backbone.Abstractions/Env.cs | 82 +++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/EventSourcing.Backbone.Abstractions/Env.cs b/EventSourcing.Backbone.Abstractions/Env.cs index 7bc9fbbd..39df7dea 100644 --- a/EventSourcing.Backbone.Abstractions/Env.cs +++ b/EventSourcing.Backbone.Abstractions/Env.cs @@ -1,11 +1,16 @@ -using static System.StringComparison; +using System.Globalization; +using System.Text.Json.Serialization; +using System.Text.Json; + +using static System.StringComparison; namespace EventSourcing.Backbone { /// /// Common Constants /// - public class Env + [JsonConverter(typeof(EnvJsonConverter))] + public class Env : IEquatable { private readonly string _value; @@ -42,6 +47,16 @@ public Env(string value) /// public static implicit operator string(Env env) => env._value; + public static bool operator ==(Env? left, Env? right) + { + return EqualityComparer.Default.Equals(left, right); + } + + public static bool operator !=(Env? left, Env? right) + { + return !(left == right); + } + #endregion // Cast overloads #region Format @@ -78,5 +93,68 @@ private static string Format(string e) public override string ToString() => _value; #endregion // ToString + + #region EnvJsonConverter + + /// + /// Env Json Converter + /// + /// + private sealed class EnvJsonConverter : JsonConverter + { + public override Env Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) => + new Env(reader.GetString()!); + + public override void Write( + Utf8JsonWriter writer, + Env env, + JsonSerializerOptions options) => + writer.WriteStringValue(env.ToString()); + } + + #endregion // EnvJsonConverter + + #region IEquatable members + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare with the current object. + /// + /// if the specified object is equal to the current object; otherwise, . + /// + public override bool Equals(object? obj) + { + return Equals(obj as Env); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// if the current object is equal to the parameter; otherwise, . + /// + public bool Equals(Env? other) + { + return other is not null && + _value == other._value; + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return _value.GetHashCode(); + } + + #endregion // IEquatable members } } From 5a0293d73b1a1a005ed20bddfe14bfec7ef7b246 Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 7 Jun 2023 18:43:35 +0000 Subject: [PATCH 134/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index e4aa2ad5..8b640c0d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.104 + 1.2.105 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 7bec8e01966a48a7b7d3c2d747b0cdada644cc81 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:10:01 +0300 Subject: [PATCH 135/178] build: fix --- Event-Sourcing-Templates.csproj | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 Event-Sourcing-Templates.csproj diff --git a/Event-Sourcing-Templates.csproj b/Event-Sourcing-Templates.csproj deleted file mode 100644 index 4b3ec16b..00000000 --- a/Event-Sourcing-Templates.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - Template - 1.0.0 - Bnaya.Event-Sourcing.Templates - AdatumCorporation Templates - Me - Templates to use when creating an application for Adatum Corporation. - dotnet-new;templates;contoso - - netstandard2.0 - - true - false - content - $(NoWarn);NU5128 - true - - - - - - - - \ No newline at end of file From eb8fa75ae247484a782cacd32032e7fa6d6f0e8d Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 7 Jun 2023 20:10:26 +0000 Subject: [PATCH 136/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8b640c0d..585c9f60 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.105 + 1.2.106 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 1fbbe0b4a24d17b08fa38e7da3f2e890e479caa2 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:11:01 +0300 Subject: [PATCH 137/178] build: update nuget --- ...ventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj | 2 +- .../EventSourcing.Backbone.IntegrationTests.csproj | 2 +- .../EventSourcing.Backbone.UnitTests.csproj | 2 +- Tests/WebSampleS3/WebSampleS3.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index 92626437..a0743a94 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index 4595bc06..a2312003 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -35,7 +35,7 @@ - + all diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj index a804c1e1..a1c2e8bf 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj @@ -8,7 +8,7 @@ - + all diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index a1e09d4f..10628994 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -19,7 +19,7 @@ - + From 10182c319bf8cfa7ca8cbd8d51bbfeb8f8c195c8 Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 7 Jun 2023 20:11:25 +0000 Subject: [PATCH 138/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 585c9f60..026ef802 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.106 + 1.2.107 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From c13d9dbbf09b3c6765554bd82e0ccdc166a3c423 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 8 Jun 2023 08:51:32 +0300 Subject: [PATCH 139/178] build: fix generated naming --- EventSourcing.Backbone.Abstractions/Env.cs | 3 +-- .../Generators/Concrete/BridgeIncrementalGenerator.cs | 9 ++++----- .../Generators/Concrete/ContractIncrementalGenerator.cs | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/EventSourcing.Backbone.Abstractions/Env.cs b/EventSourcing.Backbone.Abstractions/Env.cs index 39df7dea..cd38edd4 100644 --- a/EventSourcing.Backbone.Abstractions/Env.cs +++ b/EventSourcing.Backbone.Abstractions/Env.cs @@ -10,7 +10,7 @@ namespace EventSourcing.Backbone /// Common Constants ///
[JsonConverter(typeof(EnvJsonConverter))] - public class Env : IEquatable + public sealed class Env : IEquatable { private readonly string _value; @@ -99,7 +99,6 @@ private static string Format(string e) /// /// Env Json Converter /// - /// private sealed class EnvJsonConverter : JsonConverter { public override Env Read( diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index b583f39a..5017060c 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -53,7 +53,6 @@ protected GenInstruction[] OnGenerateConsumers( char.IsUpper(interfaceName[1]) ? interfaceName.Substring(1) : interfaceName; AssemblyName assemblyName = GetType().Assembly.GetName(); - string ns = info.Symbol.ContainingNamespace.ToDisplayString(); var dtos = EntityGenerator.GenerateEntities(prefix, info, interfaceName, generateFrom, assemblyName); GenInstruction[] gens = @@ -400,20 +399,20 @@ private static void GenerateProducerMethods( builder.Append(string.Join(", ", ps)); builder.AppendLine(")"); builder.AppendLine("\t\t{"); - builder.AppendLine($"\t\t\tvar operation = nameof({interfaceName}.{mtdName});"); + builder.AppendLine($"\t\t\tvar operation_ = nameof({interfaceName}.{mtdName});"); int i = 0; var prms = mds.Parameters; foreach (var pName in from p in prms let pName = p.Name select pName) { - builder.AppendLine($"\t\t\tvar classification{i} = CreateClassificationAdaptor(operation, nameof({pName}), {pName});"); + builder.AppendLine($"\t\t\tvar classification_{i}_ = CreateClassificationAdaptor(operation_, nameof({pName}), {pName});"); i++; } - var classifications = Enumerable.Range(0, prms.Length).Select(m => $"classification{m}"); + var classifications = Enumerable.Range(0, prms.Length).Select(m => $"classification_{m}_"); - builder.Append($"\t\t\treturn await SendAsync(operation"); + builder.Append($"\t\t\treturn await SendAsync(operation_"); if (classifications.Any()) builder.AppendLine($", {string.Join(", ", classifications)});"); else diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index 7f32bd04..c1fb64eb 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -11,12 +11,12 @@ namespace EventSourcing.Backbone { [Generator] - internal class ContractncrementalGenerator : GeneratorIncrementalBase + internal class ContractIncrementalGenerator : GeneratorIncrementalBase { private const string TARGET_ATTRIBUTE = "EventsContract"; private readonly BridgeIncrementalGenerator _bridge = new(); - public ContractncrementalGenerator() : base(TARGET_ATTRIBUTE) + public ContractIncrementalGenerator() : base(TARGET_ATTRIBUTE) { } From 0ee1cea154ca223ad6ad4b2508ea6e18588ac959 Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 8 Jun 2023 05:52:04 +0000 Subject: [PATCH 140/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 026ef802..72374813 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.107 + 1.2.108 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 803fbcef4a7f12db30e60454ec2b2137d0e14377 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 8 Jun 2023 11:24:21 +0300 Subject: [PATCH 141/178] feat: open telemetry extensions --- .../Extensions/EventSourcingExtensions.cs | 9 +- EventSourcing.Backbone.sln | 11 ++ ...g.Backbone.OpenTelemetry.Extensions.csproj | 35 ++++++ .../EventSourcingOtel.cs | 118 ++++++++++++++++++ .../Telemetry-Readme.md | 10 ++ .../icon.png | Bin 0 -> 58881 bytes ...EventSourcing.Backbone.WebEventTest.csproj | 1 + .../Program.cs | 4 +- 8 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj create mode 100644 Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs create mode 100644 Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Telemetry-Readme.md create mode 100644 Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/icon.png diff --git a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs index ad8bb49e..fe134167 100644 --- a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs +++ b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs @@ -14,10 +14,11 @@ public static class EventSourcingExtensions ///
/// The builder. /// - public static TracerProviderBuilder ListenToEventSourceRedisChannel(this TracerProviderBuilder builder) => - builder.AddSource( - EventSourceConstants.REDIS_CONSUMER_CHANNEL_SOURCE, - EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); + public static TracerProviderBuilder ListenToEventSourceRedisChannel( + this TracerProviderBuilder builder) => + builder.AddSource( + EventSourceConstants.REDIS_CONSUMER_CHANNEL_SOURCE, + EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); #region ExtractSpan diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 9e864a7b..9e02de40 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -69,6 +69,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProducingEvents", "Tests\He EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSampleS3", "Tests\WebSampleS3\WebSampleS3.csproj", "{7A749C43-60F4-4DDC-894C-5E407B08A3A9}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{2C4E653D-8B7A-48BA-A9DB-F9707BC04380}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.OpenTelemetry.Extensions", "Extensions\EventSourcing.Backbone.OpenTelemetry.Extensions\EventSourcing.Backbone.OpenTelemetry.Extensions.csproj", "{74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -177,6 +181,12 @@ Global {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {7A749C43-60F4-4DDC-894C-5E407B08A3A9}.Release|Any CPU.Build.0 = Release|Any CPU + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Gen|Any CPU.Build.0 = Gen|Any CPU + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -203,6 +213,7 @@ Global {A78DF256-6896-4246-8CF5-8CCBB27760A5} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {B528D83E-1AB9-4461-BA5E-993FF882F85B} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {7A749C43-60F4-4DDC-894C-5E407B08A3A9} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} + {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B} = {2C4E653D-8B7A-48BA-A9DB-F9707BC04380} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {103E4CA1-859C-4E6A-9F2D-7FD54A8E687C} diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj new file mode 100644 index 00000000..0fabcd90 --- /dev/null +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -0,0 +1,35 @@ + + + + EventSourcing.Backbone + https://github.com/bnayae/Event-Source-Backbone + + + + README.md + + + + + \ + True + + + + + + + + + + + + + + + + + + + + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs new file mode 100644 index 00000000..f290a43e --- /dev/null +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -0,0 +1,118 @@ +using System.Diagnostics; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Hosting; + +using OpenTelemetry.Exporter; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace Microsoft.Extensions.DependencyInjection; + +/// +/// core extensions for ASP.NET Core +/// +public static class EventSourcingOtel +{ + /// + /// Adds the open-telemetry binding. + /// + /// The services. + /// The host env. + /// The filter. + /// The sampler. + /// + public static IServiceCollection AddOpenTelemetryForEventSourcing( + this IServiceCollection services, + IHostEnvironment hostEnv, + Func? filter = null, + Sampler? sampler = null) + { + // see: + // https://opentelemetry.io/docs/instrumentation/net/getting-started/ + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + + Func filtering = filter ?? (Func)OpenTelemetryFilter; + + var appName = hostEnv.ApplicationName; + +#pragma warning disable S125 // Sections of code should not be commented out + services.AddOpenTelemetry() + .WithTracing(tracerProviderBuilder => + { + tracerProviderBuilder + .AddSource(appName) + .ConfigureResource(resource => resource + .AddService(appName)) + .AddAspNetCoreInstrumentation(m => + { + m.Filter = filtering; + // m.Enrich + m.RecordException = true; + m.EnableGrpcAspNetCoreSupport = true; + }) + .AddHttpClientInstrumentation(m => + { + // m.Enrich + m.RecordException = true; + }); + if (sampler != null) + tracerProviderBuilder.SetSampler(sampler); + tracerProviderBuilder.AddOtlpExporter(); + if (hostEnv.IsDevelopment()) + { + tracerProviderBuilder.AddConsoleExporter(options => + options.Targets = ConsoleExporterOutputTargets.Console); + } + if (Debugger.IsAttached) + tracerProviderBuilder.AddConsoleExporter(); + }) + .WithMetrics(metricsProviderBuilder => + { + metricsProviderBuilder + .ConfigureResource(resource => resource + .AddService(appName)) + .AddMeter(appName) + .AddAspNetCoreInstrumentation( + //m => { + // m.Filter = (_, ctx) => filtering(ctx); + //} + ) + .AddOtlpExporter(); + if (Debugger.IsAttached) + metricsProviderBuilder.AddConsoleExporter(); + }); + + return services; +#pragma warning restore S125 // Sections of code should not be commented out + + #region OpenTelemetryFilter + + bool OpenTelemetryFilter(HttpContext context) => OpenTelemetryFilterMap(context.Request.Path.Value); + + bool OpenTelemetryFilterMap(string? path) + { + if (string.IsNullOrEmpty(path) || + path == "/health" || + path == "/readiness" || + path == "/version" || + path == "/settings" || + path.StartsWith("/v1/kv/") || // configuration + path == "/api/v2/write" || // influx metrics + path == "/_bulk" || + path.StartsWith("/swagger") || + path.IndexOf("health-check") != -1) + { + return false; + } + return true; + } + + #endregion // OpenTelemetryFilter + } +} diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Telemetry-Readme.md b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Telemetry-Readme.md new file mode 100644 index 00000000..d805eee2 --- /dev/null +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Telemetry-Readme.md @@ -0,0 +1,10 @@ +# Open Telemetry Extensions + + +## Run Jaeger trace collector + +``` docker +docker run --name jaeger-otel --rm -it -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest +``` + +Check it on: `http://localhost:16686/search` diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/icon.png b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 37463cd6..d1fff7f3 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -42,7 +42,7 @@ Title = "Environment setup", Description = @"

Use the following docker in order to setup the environment

docker run -p 6379:6379 -it --rm --name redis-Json redislabs/rejson:latest

-

docker run --rm -it --name jaeger -p 13133:13133 -p 16686:16686 -p 4317:55680 jaegertracing/opentelemetry-all-in-one

+

docker run --name jaeger-otel --rm -it -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest

", }); }); @@ -69,7 +69,7 @@ }); IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); -services.AddOpenTelemetry(environment, shortAppName, redisConnection); +services.AddOpenTelemetryForEventSourcing(environment); services.AddOptions(); // enable usage of IOptionsSnapshot dependency injection From d11813008b689829d3b909bf08bb76b5ed393cd5 Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 8 Jun 2023 08:24:52 +0000 Subject: [PATCH 142/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 72374813..30be096a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.108 + 1.2.109 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 5b9fb619851422a716cf6a088c416f0f7be3a13d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 8 Jun 2023 12:09:56 +0300 Subject: [PATCH 143/178] buid: fix generation naming --- .../Generators/EntitiesAndHelpers/EntityGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs index c8231b85..6545f807 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/EntitiesAndHelpers/EntityGenerator.cs @@ -152,7 +152,7 @@ internal static GenInstruction GenerateEntityMapper( builder.AppendLine($"\t\t\t\t\tIConsumerPlan consumerPlan)"); builder.AppendLine($"\t\t\t\t\t\t where TCast : {interfaceName}_EntityFamily"); builder.AppendLine("\t\t\t{"); - builder.AppendLine("\t\t\t\tvar operation = announcement.Metadata.Operation;"); + builder.AppendLine("\t\t\t\tvar operation_ = announcement.Metadata.Operation;"); int j = 0; foreach (var method in item.Members) @@ -165,7 +165,7 @@ internal static GenInstruction GenerateEntityMapper( string nameOfOperetion = $"{info.Name ?? interfaceName}.{mtdName}"; string ifOrElseIf = j++ > 0 ? "else if" : "if"; - builder.AppendLine($"\t\t\t\t{ifOrElseIf}(operation == nameof({nameOfOperetion}))"); + builder.AppendLine($"\t\t\t\t{ifOrElseIf}(operation_ == nameof({nameOfOperetion}))"); builder.AppendLine("\t\t\t\t{"); builder.AppendLine($"\t\t\t\t\tif(typeof(TCast) == typeof({fullRecordName}))"); builder.AppendLine("\t\t\t\t\t{"); From 76dc4005c896e3b32c4ca46b47016d109e0bcb80 Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 8 Jun 2023 09:10:21 +0000 Subject: [PATCH 144/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 30be096a..b5659853 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.109 + 1.2.110 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From fe0e92a88636a3ae096ff25e6f46c869f2ca83de Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 8 Jun 2023 14:56:18 +0300 Subject: [PATCH 145/178] test: add web test of product cycle --- ...EventSourcing.Backbone.Abstractions.csproj | 1 + .../EventSourcingOtel.cs | 10 +- .../Controllers/ProducerController.cs | 121 ++++++++++ .../Controllers/TestController.cs | 117 +++++---- .../Jobs/ConsumerJob.cs | 227 ++++++++++++++++++ .../ProductCycle/Entities/Id.cs | 3 + .../ProductCycle/Entities/Idea.cs | 3 + .../ProductCycle/Entities/Plan.cs | 3 + .../ProductCycle/Entities/Review.cs | 4 + .../ProductCycle/Entities/Test.cs | 4 + .../ProductCycle/EventSourcingConstants.cs | 11 + .../Extensions/ConsumerExtensions.cs | 47 ++++ .../ProductCycleProducerExtensions.cs | 42 ++++ .../ProductCycle/IProductCycle.cs | 23 ++ .../ProductCycle/NextStage.cs | 10 + .../Program.cs | 10 +- .../ISample.cs | 4 +- 17 files changed, 571 insertions(+), 69 deletions(-) create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Id.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Plan.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Review.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Test.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/EventSourcingConstants.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/NextStage.cs diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index 68bd4171..ba0bc0bd 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -23,6 +23,7 @@ + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index f290a43e..01733350 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -47,8 +47,7 @@ public static IServiceCollection AddOpenTelemetryForEventSourcing( { tracerProviderBuilder .AddSource(appName) - .ConfigureResource(resource => resource - .AddService(appName)) + .ConfigureResource(resource => resource.AddService(appName)) .AddAspNetCoreInstrumentation(m => { m.Filter = filtering; @@ -69,14 +68,11 @@ public static IServiceCollection AddOpenTelemetryForEventSourcing( tracerProviderBuilder.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Console); } - if (Debugger.IsAttached) - tracerProviderBuilder.AddConsoleExporter(); }) .WithMetrics(metricsProviderBuilder => { metricsProviderBuilder - .ConfigureResource(resource => resource - .AddService(appName)) + .ConfigureResource(resource => resource.AddService(appName)) .AddMeter(appName) .AddAspNetCoreInstrumentation( //m => { @@ -84,7 +80,7 @@ public static IServiceCollection AddOpenTelemetryForEventSourcing( //} ) .AddOtlpExporter(); - if (Debugger.IsAttached) + if (hostEnv.IsDevelopment()) metricsProviderBuilder.AddConsoleExporter(); }); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs new file mode 100644 index 00000000..7e07d724 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs @@ -0,0 +1,121 @@ +using EventSourcing.Backbone; + +using Microsoft.AspNetCore.Mvc; + +using EventSourcing.Backbone.WebEventTest; + +namespace EventSourcing.Backbone.WebEventTest.Controllers; + +[ApiController] +[Route("[controller]")] +public class ProducerController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IProductCycleProducer _producer; + + public ProducerController( + ILogger logger, + IProductCycleProducer producer) + { + _logger = logger; + _producer = producer; + } + + /// + /// Post order state. + /// + /// The payload. + /// + [HttpPost("idea")] + [ProducesResponseType(StatusCodes.Status201Created)] + //[AllowAnonymous] + public async Task IdeaAsync(Idea payload) + { + var (title, describe) = payload; + _logger.LogDebug("Sending idea event"); + EventKey key = await _producer.IdeaAsync(title, describe); + return key; + } + + /// + /// Post packing state. + /// + /// The payload. + /// + [HttpPost("plan")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task PlanAsync([FromBody] Plan payload) + { + var (id_, describe) = payload; + var (id, version) = id_; + _logger.LogDebug("Sending plan event"); + EventKey key = await _producer.PlanedAsync(id, version, describe); + return key; + } + + /// + /// Post on-delivery state. + /// + /// The payload. + /// + [HttpPost("review")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task ReviewAsync([FromBody] Review payload) + { + var (id_, notes) = payload; + var (id, version) = id_; + + _logger.LogDebug("Sending review event"); + EventKey key = await _producer.ReviewedAsync(id, version, notes); + return key; + } + + /// + /// Post on-received state. + /// + /// The payload. + /// + [HttpPost("implement")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task ImplementAsync([FromBody] Id payload) + { + var (id, version) = payload; + + _logger.LogDebug("Sending implement event"); + EventKey key = await _producer.ImplementedAsync(id, version); + return key; + } + + /// + /// Post on-received state. + /// + /// The payload. + /// + [HttpPost("test")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task TestAsync([FromBody] Test payload) + { + var (id_, notes) = payload; + var (id, version) = id_; + + _logger.LogDebug("Sending test event"); + EventKey key = await _producer.TestedAsync(id, version, notes); + return key; + } + + /// + /// Post on-received state. + /// + /// The payload. + /// + [HttpPost("Deploy")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task DeployAsync([FromBody] Id payload) + { + var (id, version) = payload; + + _logger.LogDebug("Sending deploy event"); + EventKey key = await _producer.DeployedAsync(id, version); + return key; + } +} \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs index 0c3f6d16..4a5e04ed 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs @@ -2,74 +2,73 @@ using StackExchange.Redis; -namespace EventSourcing.Backbone.WebEventTest.Controllers +namespace EventSourcing.Backbone.WebEventTest.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class TestController : ControllerBase { - [Route("api/[controller]")] - [ApiController] - public class TestController : ControllerBase - { - private readonly ILogger _logger; - private readonly IEventSourceRedisConnectionFactory _connFacroty; + private readonly ILogger _logger; + private readonly IEventSourceRedisConnectionFactory _connFacroty; - public TestController( - ILogger logger, - IEventSourceRedisConnectionFactory connFacroty) - { - _logger = logger; - _connFacroty = connFacroty; - } + public TestController( + ILogger logger, + IEventSourceRedisConnectionFactory connFacroty) + { + _logger = logger; + _connFacroty = connFacroty; + } - /// - /// Post Analysts - /// - /// - /// - [HttpGet("conn")] - //[AllowAnonymous] - [ProducesResponseType(StatusCodes.Status201Created)] - public async ValueTask GetAsync() - { - var conn = await RedisClientFactory.CreateProviderAsync(logger: _logger); - var status = conn.GetStatus(); - var db = conn.GetDatabase(); - var p = await db.PingAsync(); + /// + /// Post Analysts + /// + /// + /// + [HttpGet("conn")] + //[AllowAnonymous] + [ProducesResponseType(StatusCodes.Status201Created)] + public async ValueTask GetAsync() + { + var conn = await RedisClientFactory.CreateProviderAsync(logger: _logger); + var status = conn.GetStatus(); + var db = conn.GetDatabase(); + var p = await db.PingAsync(); - return @$"ClientName: {conn.ClientName}, IsConnected: {conn.IsConnected}, + return @$"ClientName: {conn.ClientName}, IsConnected: {conn.IsConnected}, timeout (ms): {conn.TimeoutMilliseconds}, status: {status}, ping: {p}"; - } + } - /// - /// Post Analysts - /// - /// - /// - [HttpGet("ping")] - //[AllowAnonymous] - [ProducesResponseType(StatusCodes.Status201Created)] - public async ValueTask GetPingAsync() - { - IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(); - var p = await db.PingAsync(); - _logger.LogInformation("Schema: {schema}", Request.Scheme); - return p; - } + /// + /// Post Analysts + /// + /// + /// + [HttpGet("ping")] + //[AllowAnonymous] + [ProducesResponseType(StatusCodes.Status201Created)] + public async ValueTask GetPingAsync() + { + IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(); + var p = await db.PingAsync(); + _logger.LogInformation("Schema: {schema}", Request.Scheme); + return p; + } - /// - /// Post Analysts - /// - /// - /// - [HttpGet("static")] - //[AllowAnonymous] - [ProducesResponseType(StatusCodes.Status201Created)] - public async ValueTask GetStaticAsync() - { - IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(); - var p = await db.PingAsync(); + /// + /// Post Analysts + /// + /// + /// + [HttpGet("static")] + //[AllowAnonymous] + [ProducesResponseType(StatusCodes.Status201Created)] + public async ValueTask GetStaticAsync() + { + IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(); + var p = await db.PingAsync(); - return @$"ping: {p}"; - } + return @$"ping: {p}"; } } diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs new file mode 100644 index 00000000..c4c2d6dc --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs @@ -0,0 +1,227 @@ +using EventSourcing.Backbone; +using EventSourcing.Backbone.Building; + +namespace EventSourcing.Backbone.WebEventTest; + +/// +/// Consumer job +/// +/// +/// +/// +public sealed class ConsumerJob : IHostedService, IProductCycleConsumer +{ + private readonly IConsumerSubscribeBuilder _builder; + private CancellationTokenSource? _cancellationTokenSource; + private IConsumerLifetime? _subscription; + + private readonly ILogger _logger; + private readonly IProductCycleProducer _producer; + + #region Ctor + + /// + /// Initializes a new instance. + /// + /// The logger. + /// The builder. + /// The producer. + public ConsumerJob( + ILogger logger, + IKeyed consumerBuilderKeyed, + IProductCycleProducer producer) + { + if (!consumerBuilderKeyed.TryGet(EventSourcingConstants.URI, out var consumerBuilder)) throw new EventSourcingException("consumer is missing DI"); + _builder = consumerBuilder.WithLogger(logger); + _logger = logger; + _producer = producer; + } + + #endregion Ctor + + #region OnStartAsync + + /// + /// Start Consumer Job. + /// + /// The cancellation token. + Task IHostedService.StartAsync(CancellationToken cancellationToken) + { + _cancellationTokenSource = new CancellationTokenSource(); + var canellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); + _subscription = _builder + .Group(EventSourcingConstants.CONSUMER_GROUP) + .WithCancellation(canellation.Token) + // this extension is generate (if you change the interface use the correlated new generated extension method) + .SubscribeProductCycleConsumer(this); + + return Task.CompletedTask; + } + + #endregion // OnStartAsync + + #region StopAsync + + /// + /// Stops the Consumer Job. + /// + /// The cancellation token. + /// + async Task IHostedService.StopAsync(CancellationToken cancellationToken) + { + _cancellationTokenSource?.CancelSafe(); + await (_subscription?.Completion ?? Task.CompletedTask); + } + + #endregion // StopAsync + + // TODO: enrich telemetry + + async ValueTask IProductCycleConsumer.IdeaAsync(ConsumerMetadata consumerMetadata, string title, string describe) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {title} + Let's build a plan..... + """, meta.Operation, meta.MessageId, title); + await Task.Delay(Environment.TickCount % 1000 + 100); + + await _producer.PlanedAsync(meta.MessageId, new Version(0, 0, 1, 0), "Just do it this way...."); + await consumerMetadata.AckAsync(); // not required when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.PlanedAsync(ConsumerMetadata consumerMetadata, string id, Version version, string doc) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + {doc} + --- + Now we'll review it than, either approve or reject the plan. + """, meta.Operation, id, version, doc); + + int delay = Environment.TickCount % 2_000 + 500; + await Task.Delay(delay); + if (delay < 550) + await _producer.RejectedAsync(id, version, meta.Operation, NextStage.Abandon, "Seems to complex"); + else if (delay < 600) + await _producer.RejectedAsync(id, version, meta.Operation, NextStage.Reject, "Need some refinement about ..."); + else + await _producer.ReviewedAsync(id, version, "Great plan"); + await consumerMetadata.AckAsync(); // not required when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.ReviewedAsync(ConsumerMetadata consumerMetadata, string id, Version version, string[] notes) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + {notes} + --- + Let's implement it! + """, meta.Operation, id, version, string.Join("\r\n- ", notes)); + + int delay = Environment.TickCount % 5_000 + 1_500; + await Task.Delay(delay); + await _producer.ImplementedAsync(id, version); + await consumerMetadata.AckAsync(); // not required when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.ImplementedAsync(ConsumerMetadata consumerMetadata, string id, Version version) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + Now it's time for QA. + """, meta.Operation, id, version); + + int delay = Environment.TickCount % 3_000 + 100; + await Task.Delay(delay); + if (delay < 600) + await _producer.RejectedAsync(id, version, meta.Operation, NextStage.Reject, "Performance doesn't meet our SLA..."); + else + await _producer.TestedAsync(id, version, "Ready for deployment"); + await consumerMetadata.AckAsync(); // not required when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.TestedAsync(ConsumerMetadata consumerMetadata, string id, Version version, string[] notes) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + {notes} + --- + Start deploying + """, meta.Operation, id, version, string.Join("\r\n- ", notes)); + + int delay = Environment.TickCount % 2_000 + 100; + await Task.Delay(delay); + await _producer.DeployedAsync(id, version); + + if (delay < 1_500) + { + _logger.Log(level, "planing next iteration..."); + await _producer.DeployedAsync(id, version); + } + + await consumerMetadata.AckAsync(); // not required when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.DeployedAsync(ConsumerMetadata consumerMetadata, string id, Version version) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, "handling {event} [{id}]: {version}", meta.Operation, id, version); + int delay = Environment.TickCount % 2_000 + 100; + if (delay > 600) + { + await Task.Delay(delay); + _logger.Log(level, "planing next iteration..."); + await _producer.PlanedAsync(id, new Version(version.Major, version.Minor, version.Build, version.Revision + 1), "improving xyz..."); + } + } + + async ValueTask IProductCycleConsumer.RejectedAsync(ConsumerMetadata consumerMetadata, + string id, + System.Version version, + string operation, + NextStage nextStage, + string[] notes) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + operation = {operation} + + {notes} + --- + """, meta.Operation, id, version, operation, string.Join("\r\n- ", notes)); + + if (nextStage != NextStage.Abandon) + { + string message = operation switch + { + nameof(IProductCycleConsumer.ImplementedAsync) => "Fix bugs", + nameof(IProductCycleConsumer.PlanedAsync) => "Improve plans", + _ => string.Empty + }; + _logger.Log(level, "Re-plan"); + await Task.Delay(1000); + await _producer.PlanedAsync(id, version, message); + } + + await consumerMetadata.AckAsync(); // not required when configuring the consumer to Ack on success. + } +} + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Id.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Id.cs new file mode 100644 index 00000000..238488fd --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Id.cs @@ -0,0 +1,3 @@ +namespace EventSourcing.Backbone.WebEventTest; + +public record Id(string id, Version version); \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs new file mode 100644 index 00000000..4d3c6cc9 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs @@ -0,0 +1,3 @@ +namespace EventSourcing.Backbone.WebEventTest; + +public record Idea (string title, string describe); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Plan.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Plan.cs new file mode 100644 index 00000000..7068ad21 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Plan.cs @@ -0,0 +1,3 @@ +namespace EventSourcing.Backbone.WebEventTest; + +public record Plan(Id id, string describe); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Review.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Review.cs new file mode 100644 index 00000000..b8f88eb1 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Review.cs @@ -0,0 +1,4 @@ + +namespace EventSourcing.Backbone.WebEventTest; + +public record Review(Id id, params string[] notes); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Test.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Test.cs new file mode 100644 index 00000000..0b9fc624 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Test.cs @@ -0,0 +1,4 @@ + +namespace EventSourcing.Backbone.WebEventTest; + +public record Test(Id id, params string[] notes); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/EventSourcingConstants.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/EventSourcingConstants.cs new file mode 100644 index 00000000..d951d6b5 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/EventSourcingConstants.cs @@ -0,0 +1,11 @@ +namespace EventSourcing.Backbone.WebEventTest; + +/// +/// Common constants +/// +public static class EventSourcingConstants +{ + public const string URI = "CHANGE-ME"; + public const string S3_BUCKET = "event-sourcing-change-me"; + public const string CONSUMER_GROUP = "CHANGE_ME-EVENT-SOURCING-CONSUMER-GROUP"; +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs new file mode 100644 index 00000000..3be72e10 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs @@ -0,0 +1,47 @@ +using EventSourcing.Backbone; + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace EventSourcing.Backbone.WebEventTest; + +/// +/// DI Extensions for ASP.NET Core +/// +public static class ConsumerExtensions +{ + /// + /// Adds the shipment tracking producer. + /// + /// The services. + /// The URI. + /// The s3 bucket. + /// The environment. + /// + public static IServiceCollection AddConsumer + ( + this IServiceCollection services, + string uri, + string s3Bucket, + Env env) + { + var s3Options = new S3Options { Bucket = s3Bucket }; + services.AddKeyedSingleton( ioc => + { + IConsumerReadyBuilder consumer = + ioc.ResolveRedisConsumerChannel() + .ResolveS3Storage(s3Options) + .WithOptions(o => o with + { + TraceAsParent = TimeSpan.FromMinutes(10), + OriginFilter = MessageOrigin.Original, + AckBehavior = AckBehavior.OnSucceed + }) + .Environment(env) + .Uri(uri); + return consumer; + }, EventSourcingConstants.URI); + + return services; + } +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs new file mode 100644 index 00000000..9ac12550 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs @@ -0,0 +1,42 @@ +using EventSourcing.Backbone; + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace EventSourcing.Backbone.WebEventTest; + +/// +/// DI Extensions for ASP.NET Core +/// +public static class ProductCycleProducerExtensions +{ + /// + /// Adds the shipment tracking producer. + /// + /// The services. + /// The URI. + /// The environment. + /// + public static IServiceCollection AddProductCycleProducer + ( + this IServiceCollection services, + string uri, + string s3Bucket, + Env env) + { + var s3Options = new S3Options { Bucket = s3Bucket }; + services.AddSingleton(ioc => + { + ILogger logger = ioc.GetService>() ?? throw new EventSourcingException("Logger is missing"); + IProductCycleProducer producer = ioc.ResolveRedisProducerChannel() + .ResolveS3Storage(s3Options) + .Environment(env) + .Uri(uri) + .WithLogger(logger) + .BuildProductCycleProducer(); + return producer; + }); + + return services; + } +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs new file mode 100644 index 00000000..d506413a --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs @@ -0,0 +1,23 @@ +#pragma warning disable S1133 // Deprecated code should be removed + +using EventSourcing.Backbone; + +namespace EventSourcing.Backbone.WebEventTest; + +/// +/// Event's schema definition +/// Return type of each method should be +/// +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +[Obsolete("Either use the Producer or Consumer version of this interface", true)] +public interface IProductCycle +{ + ValueTask IdeaAsync(string title, string describe); + ValueTask PlanedAsync(string id, Version version, string doc); + ValueTask ReviewedAsync(string id, Version version, params string[] notes); + ValueTask ImplementedAsync(string id, Version version); + ValueTask TestedAsync(string id, Version version, params string[] notes); + ValueTask DeployedAsync(string id, Version version); + ValueTask RejectedAsync(string id, Version version, string operation, NextStage nextStage, params string[] notes); +} \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/NextStage.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/NextStage.cs new file mode 100644 index 00000000..03e9f1bc --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/NextStage.cs @@ -0,0 +1,10 @@ +#pragma warning disable S1133 // Deprecated code should be removed + + +namespace EventSourcing.Backbone.WebEventTest; + +public enum NextStage +{ + Reject, + Abandon +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index d1fff7f3..ece55587 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -46,6 +46,11 @@ ", }); }); + +services.AddProductCycleProducer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); +services.AddConsumer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); + +services.AddHostedService(); services.AddHostedService(); services.AddHostedService(); string fwPort = Environment.GetEnvironmentVariable("FW") ?? "MISSING-PORT"; @@ -69,11 +74,14 @@ }); IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); -services.AddOpenTelemetryForEventSourcing(environment); +//services.AddOpenTelemetryForEventSourcing(environment); +services.AddOpenTelemetry(environment, shortAppName, redisConnection); services.AddOptions(); // enable usage of IOptionsSnapshot dependency injection services.AddEventSourceRedisConnection(); +services.AddConsumer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); +services.AddProductCycleProducer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); services.AddEventSource(env); void RedisConfigEnrichment(ConfigurationOptions configuration) diff --git a/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs index 3f9724e9..91dd7f88 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen.Playground/ISample.cs @@ -11,9 +11,9 @@ public interface ISample /// Execute. ///
/// The i. - /// The s. + /// The operation. /// The notes. /// - ValueTask ExecAsync(int i, string s, params string[] notes); + ValueTask ExecAsync(int i, string operation, params string[] notes); } } From a86f87d2d7fd621307094b04c7c2a9b32d64a19b Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:17:19 +0300 Subject: [PATCH 146/178] rfc: xml doc --- .../Extensions/ProductCycle/ProductCycleProducerExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs index 9ac12550..e916bee3 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs @@ -15,6 +15,7 @@ public static class ProductCycleProducerExtensions ///
/// The services. /// The URI. + /// The s3 bucket. /// The environment. /// public static IServiceCollection AddProductCycleProducer From 90f8f666554ac729a76e2a6e3cb4862b8496390e Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 8 Jun 2023 12:17:42 +0000 Subject: [PATCH 147/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index b5659853..51de7cab 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.110 + 1.2.111 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From e33086c41210fe163632b497778a51a3ed0be20a Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:51:37 +0300 Subject: [PATCH 148/178] feat: fix redis sources (telemetry) --- .../EventSourcingOtel.cs | 34 +++++++++++++++++-- .../Program.cs | 18 +++++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index 01733350..1edf01f6 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -19,6 +19,26 @@ namespace Microsoft.Extensions.DependencyInjection; ///
public static class EventSourcingOtel { + /// + /// The name of redis consumer channel source + /// + public const string REDIS_CONSUMER_CHANNEL_SOURCE = "redis-consumer-channel"; + /// + /// The name of redis producer channel source + /// + public const string REDIS_PRODUCER_CHANNEL_SOURCE = "redis-producer-channel"; + + /// + /// Adds the event consumer telemetry source (will result in tracing the consumer). + /// + /// The builder. + /// + private static TracerProviderBuilder ListenToEventSourceRedisChannel( + this TracerProviderBuilder builder) => + builder.AddSource( + REDIS_CONSUMER_CHANNEL_SOURCE, + REDIS_PRODUCER_CHANNEL_SOURCE); + /// /// Adds the open-telemetry binding. /// @@ -26,12 +46,14 @@ public static class EventSourcingOtel /// The host env. /// The filter. /// The sampler. + /// The additional list of subscribe sources for the telemetry. /// public static IServiceCollection AddOpenTelemetryForEventSourcing( this IServiceCollection services, IHostEnvironment hostEnv, Func? filter = null, - Sampler? sampler = null) + Sampler? sampler = null, + params string[] additionalSources) { // see: // https://opentelemetry.io/docs/instrumentation/net/getting-started/ @@ -45,8 +67,16 @@ public static IServiceCollection AddOpenTelemetryForEventSourcing( services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => { + var sources = new[] { appName, + REDIS_CONSUMER_CHANNEL_SOURCE, + REDIS_PRODUCER_CHANNEL_SOURCE}; + if(additionalSources != null && additionalSources.Length != 0) + { + sources = sources.Concat(additionalSources).ToArray(); + } + tracerProviderBuilder - .AddSource(appName) + .AddSource(sources) .ConfigureResource(resource => resource.AddService(appName)) .AddAspNetCoreInstrumentation(m => { diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index ece55587..c1e7bf2b 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -73,9 +73,9 @@ c.DefaultRequestHeaders.Add("wk-pattern", "migration"); }); -IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); -//services.AddOpenTelemetryForEventSourcing(environment); -services.AddOpenTelemetry(environment, shortAppName, redisConnection); +//IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); +services.AddOpenTelemetryForEventSourcing(environment); +//services.AddOpenTelemetry(environment, shortAppName, redisConnection); services.AddOptions(); // enable usage of IOptionsSnapshot dependency injection @@ -84,12 +84,12 @@ services.AddProductCycleProducer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); services.AddEventSource(env); -void RedisConfigEnrichment(ConfigurationOptions configuration) -{ - configuration.ReconnectRetryPolicy = new RedisReconnectRetryPolicy(); - configuration.ClientName = "Web Test"; -} -services.AddSingleton>(RedisConfigEnrichment); +//void RedisConfigEnrichment(ConfigurationOptions configuration) +//{ +// configuration.ReconnectRetryPolicy = new RedisReconnectRetryPolicy(); +// configuration.ClientName = "Web Test"; +//} +//services.AddSingleton>(RedisConfigEnrichment); services.AddControllers() .WithJsonOptions(); From 38cee9484b0ed3e4edfc68cd5918078489486d5a Mon Sep 17 00:00:00 2001 From: bnayae Date: Thu, 8 Jun 2023 12:52:05 +0000 Subject: [PATCH 149/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 51de7cab..2c1fb07f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.111 + 1.2.112 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 761392040d8db9c832748e681b69d9b1de1d74ed Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 11 Jun 2023 15:19:29 +0300 Subject: [PATCH 150/178] feat: telemetry filter --- .../EventSourcingOtel.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index 1edf01f6..188addd4 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -110,8 +110,8 @@ public static IServiceCollection AddOpenTelemetryForEventSourcing( //} ) .AddOtlpExporter(); - if (hostEnv.IsDevelopment()) - metricsProviderBuilder.AddConsoleExporter(); + //if (hostEnv.IsDevelopment()) + // metricsProviderBuilder.AddConsoleExporter(); }); return services; @@ -132,7 +132,9 @@ bool OpenTelemetryFilterMap(string? path) path == "/api/v2/write" || // influx metrics path == "/_bulk" || path.StartsWith("/swagger") || - path.IndexOf("health-check") != -1) + path.IndexOf("health-check") != -1 || + path == "/_framework/aspnetcore-browser-refresh.js" || + path.StartsWith("/_vs/")) { return false; } From 7635badcce1b7b9fe7f406f2aae621bffe42666c Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 11 Jun 2023 12:19:55 +0000 Subject: [PATCH 151/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2c1fb07f..8b92dc55 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.112 + 1.2.113 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From a425a212fdbf70614627b9b624a040ddb0121b8a Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 11 Jun 2023 15:27:24 +0300 Subject: [PATCH 152/178] build: nuget update --- ...ventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj | 2 +- Tests/WebSampleS3/WebSampleS3.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index a0743a94..81cc428c 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index 10628994..650e92c7 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -19,7 +19,7 @@ - + From c01c5eb5e526d34597b863dbf98395eb27c4a34c Mon Sep 17 00:00:00 2001 From: bnayae Date: Sun, 11 Jun 2023 12:27:50 +0000 Subject: [PATCH 153/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8b92dc55..96111474 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.113 + 1.2.114 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 8d5efc23d0a2357f7d5c1d4f3e6a5563154302ee Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 12 Jun 2023 14:13:36 +0300 Subject: [PATCH 154/178] feat: tolerate s3 setting --- .../RedisConsumerChannel.cs | 16 ++ .../S3ConsumerStorageStrategyExtension.cs | 14 +- .../S3ProducerStorageStrategyExtension.cs | 15 +- .../S3Repository.cs | 10 +- EventSourcing.Backbone.sln | 30 ++++ .../ProductCycleConsumerController.cs | 37 ++++ .../Dockerfile | 22 +++ .../Extensions/ConsumerExtensions.cs | 78 ++++++++ .../Jobs/ProductCycleConsumerJob.cs | 167 ++++++++++++++++++ .../Program.cs | 68 +++++++ .../Properties/launchSettings.json | 48 +++++ ...ckup.Events.ConsumerWebTest.Service.csproj | 30 ++++ ...ests.Events.ConsumerWebTest.Service.csproj | 40 +++++ .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../icon.png | Bin 0 -> 58881 bytes .../ProductCycleProducerController.cs | 122 +++++++++++++ .../Dockerfile | 22 +++ .../Entities/Id.cs | 3 + .../Entities/Idea.cs | 3 + .../Entities/Plan.cs | 3 + .../Entities/Review.cs | 5 + .../Entities/Test.cs | 5 + .../ProductCycleProducerExtensions.cs | 70 ++++++++ .../Program.cs | 67 +++++++ .../Properties/launchSettings.json | 48 +++++ ...ests.Events.ProducerWebTest.Service.csproj | 43 +++++ .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../icon.png | Bin 0 -> 58881 bytes .../IProductCycle.cs | 23 +++ .../NextStage.cs | 7 + .../ProductCycleConstants.cs | 10 ++ .../Tests.Events.WebTest.Abstractions.csproj | 18 ++ .../icon.png | Bin 0 -> 58881 bytes 35 files changed, 1050 insertions(+), 8 deletions(-) create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Dockerfile create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Properties/launchSettings.json create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests - Backup.Events.ConsumerWebTest.Service.csproj create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.Development.json create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.json create mode 100644 Tests/Specialized/Tests.Events.ConsumerWebTest.Service/icon.png create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Controllers/ProductCycleProducerController.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Dockerfile create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Id.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Plan.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Properties/launchSettings.json create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.Development.json create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.json create mode 100644 Tests/Specialized/Tests.Events.ProducerWebTest.Service/icon.png create mode 100644 Tests/Specialized/Tests.Events.WebTest.Abstractions/IProductCycle.cs create mode 100644 Tests/Specialized/Tests.Events.WebTest.Abstractions/NextStage.cs create mode 100644 Tests/Specialized/Tests.Events.WebTest.Abstractions/ProductCycleConstants.cs create mode 100644 Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj create mode 100644 Tests/Specialized/Tests.Events.WebTest.Abstractions/icon.png diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 811e7090..f8ec4de9 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -478,6 +478,22 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( plan.ConsumerGroup, logger); } + catch (RedisServerException ex) + { + logger?.LogWarning(ex, ex.Message); + //await _connFactory.CreateConsumerGroupIfNotExistsAsync( + // key, + // plan.ConsumerGroup, + // logger); + } + catch (Exception ex) + { + logger?.LogWarning(ex, ex.Message); + //await _connFactory.CreateConsumerGroupIfNotExistsAsync( + // key, + // plan.ConsumerGroup, + // logger); + } #endregion // Exception Handling } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs index 84d97bdf..0ba31042 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategyExtension.cs @@ -90,9 +90,17 @@ public static IConsumerStoreStrategyBuilder ResolveS3Storage( S3Options options = default, EventBucketCategories targetType = EventBucketCategories.All) { - IAmazonS3 s3Client = builder.ServiceProvider.GetService() ?? throw new EventSourcingException("IAmazonS3 is not registered"); - var result = builder.AddS3Storage(s3Client, options, targetType); - return result; + ILogger? logger = builder.ServiceProvider.GetService>(); + IAmazonS3? s3Client = builder.ServiceProvider.GetService(); + if (s3Client != null) + { + var injectionResult = builder.AddS3Storage(s3Client, options, targetType); + logger?.LogInformation("Consumer, Resolving AWS S3 via IAmazonS3 injection (might be via profile)"); + return injectionResult; + } + logger?.LogInformation("Consumer, Resolving AWS S3 via environment variable"); + var envVarResult = builder.AddS3Storage(options, targetType); + return envVarResult; } } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs index cb0f48e5..c0e4be9b 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategyExtension.cs @@ -93,9 +93,18 @@ public static IProducerStoreStrategyBuilder ResolveS3Storage( EventBucketCategories targetType = EventBucketCategories.All, Predicate? filter = null) { - IAmazonS3 s3Client = builder.ServiceProvider.GetService() ?? throw new EventSourcingException("IAmazonS3 is not registered"); - IProducerStoreStrategyBuilder result = builder.AddS3Storage(s3Client, options, targetType, filter); - return result; + ILogger? logger = builder.ServiceProvider.GetService>(); + IAmazonS3? s3Client = builder.ServiceProvider.GetService(); + + if (s3Client != null) + { + IProducerStoreStrategyBuilder injectionResult = builder.AddS3Storage(s3Client, options, targetType, filter); + logger?.LogInformation("Producer, Resolving AWS S3 via IAmazonS3 injection (might be via profile)"); + return injectionResult; + } + logger?.LogInformation("Producer, Resolving AWS S3 via environment variable"); + IProducerStoreStrategyBuilder envVarResult = builder.AddS3Storage(options, targetType, filter); + return envVarResult; } } } diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs index a9d142bf..094fe00b 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/S3Repository.cs @@ -377,8 +377,14 @@ public async ValueTask SaveAsync( } catch { } _logger.LogError(e.FormatLazy(), - "AWS-S3 Failed to write: {payload}, {env}, {id}, {bucket}, {key}", json, env, id, bucket, key); - string msg = $"AWS-S3 Failed to write: {env}, {id}, {bucket}, {key}"; + """ + AWS-S3 Failed to write: {payload}, {env}, {id}, {bucket}, {key} + Make sure to that the bucket exists & credentials sets right. + """, json, env, id, bucket, key); + string msg = $""" + AWS-S3 Failed to write: {env}, {id}, {bucket}, {key} + Make sure to that the bucket exists & credentials sets right. + """; throw new EventSourcingException(msg, e); } catch (Exception e) diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 9e02de40..1798ad5a 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -73,6 +73,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventSourcing.Backbone.OpenTelemetry.Extensions", "Extensions\EventSourcing.Backbone.OpenTelemetry.Extensions\EventSourcing.Backbone.OpenTelemetry.Extensions.csproj", "{74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specialized", "Specialized", "{DB106708-EA65-4FD3-9362-950CA07DB2E6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Events.WebTest.Abstractions", "Tests\Specialized\Tests.Events.WebTest.Abstractions\Tests.Events.WebTest.Abstractions.csproj", "{8E90962C-870D-49C6-A260-585D16F13614}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Events.ProducerWebTest.Service", "Tests\Specialized\Tests.Events.ProducerWebTest.Service\Tests.Events.ProducerWebTest.Service.csproj", "{ED906FBD-F8BB-40D5-8629-00CE0F963681}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Events.ConsumerWebTest.Service", "Tests\Specialized\Tests.Events.ConsumerWebTest.Service\Tests.Events.ConsumerWebTest.Service.csproj", "{842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -187,6 +195,24 @@ Global {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Gen|Any CPU.Build.0 = Gen|Any CPU {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Release|Any CPU.Build.0 = Release|Any CPU + {8E90962C-870D-49C6-A260-585D16F13614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E90962C-870D-49C6-A260-585D16F13614}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E90962C-870D-49C6-A260-585D16F13614}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {8E90962C-870D-49C6-A260-585D16F13614}.Gen|Any CPU.Build.0 = Gen|Any CPU + {8E90962C-870D-49C6-A260-585D16F13614}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E90962C-870D-49C6-A260-585D16F13614}.Release|Any CPU.Build.0 = Release|Any CPU + {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Gen|Any CPU.Build.0 = Gen|Any CPU + {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Release|Any CPU.Build.0 = Release|Any CPU + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Gen|Any CPU.Build.0 = Gen|Any CPU + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -214,6 +240,10 @@ Global {B528D83E-1AB9-4461-BA5E-993FF882F85B} = {9A1275C5-2299-4560-ABB6-CE56B9175CF7} {7A749C43-60F4-4DDC-894C-5E407B08A3A9} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B} = {2C4E653D-8B7A-48BA-A9DB-F9707BC04380} + {DB106708-EA65-4FD3-9362-950CA07DB2E6} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} + {8E90962C-870D-49C6-A260-585D16F13614} = {DB106708-EA65-4FD3-9362-950CA07DB2E6} + {ED906FBD-F8BB-40D5-8629-00CE0F963681} = {DB106708-EA65-4FD3-9362-950CA07DB2E6} + {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F} = {DB106708-EA65-4FD3-9362-950CA07DB2E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {103E4CA1-859C-4E6A-9F2D-7FD54A8E687C} diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs new file mode 100644 index 00000000..161c43ad --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs @@ -0,0 +1,37 @@ +using Tests.Events.WebTest.Abstractions; +using System.Text.Json; +using EventSourcing.Backbone; +using Microsoft.AspNetCore.Mvc; + +namespace Tests.Events.ConsumerWebTest.Controllers; + +[ApiController] +[Route("[controller]")] +public class ProductCycleConsumerController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IConsumerReceiver _receiver; + + public ProductCycleConsumerController( + ILogger logger, + IKeyed consumerBuilderKeyed) + { + _logger = logger; + if (!consumerBuilderKeyed.TryGet(ProductCycleConstants.URI, out var consumerBuilder)) + throw new EventSourcingException($"The Consumer's registration found under the [{ProductCycleConstants.URI}] key."); + _receiver = consumerBuilder.BuildReceiver(); + } + + /// + /// Gets an event by event key. + /// + /// The event key. + /// + [HttpGet("{eventKey}")] + public async Task GetAsync(string eventKey) + { + _logger.LogDebug("fetching event [{key}]", eventKey); + var json = await _receiver.GetJsonByIdAsync(eventKey); + return json; + } +} \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Dockerfile b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Dockerfile new file mode 100644 index 00000000..71b11d4c --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Dockerfile @@ -0,0 +1,22 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["Tests.Events.ConsumerWebTest/Tests.Events.ConsumerWebTest.csproj", "Tests.Events.ConsumerWebTest/"] +RUN dotnet restore "Tests.Events.ConsumerWebTest/Tests.Events.ConsumerWebTest.csproj" +COPY . . +WORKDIR "/src/Tests.Events.ConsumerWebTest" +RUN dotnet build "Tests.Events.ConsumerWebTest.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Tests.Events.ConsumerWebTest.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Tests.Events.ConsumerWebTest.dll"] \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs new file mode 100644 index 00000000..8e4235e7 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs @@ -0,0 +1,78 @@ +using EventSourcing.Backbone; + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + +namespace Tests.Events.ConsumerWebTest; + +/// +/// DI Extensions for ASP.NET Core +/// +public static class ConsumerExtensions +{ + /// + /// Register a consumer. + /// + /// The services. + /// The URI. + /// The environment. + /// + public static IServiceCollection AddConsumer ( + this IServiceCollection services, + string uri, + Env env) + { + services.AddSingleton(ioc => + { + return BuildConsumer(uri, env, ioc + ); + }); + + return services; + } + + /// + /// Register a consumer when the URI of the service used as the registration's key. + /// See: https://medium.com/weknow-network/keyed-dependency-injection-using-net-630bd73d3672 + /// + /// The services. + /// The URI of the stream (which is also used as the DI key). + /// The environment. + /// + public static IServiceCollection AddKeyedConsumer ( + this IServiceCollection services, + string uri, + Env env) + { + services.AddKeyedSingleton(ioc => + { + return BuildConsumer(uri + , env + , ioc + ); + }, uri); + + return services; + } + + /// + /// Builds the consumer. + /// + /// The URI. + /// The environment. + /// The DI provider. + /// + private static IConsumerReadyBuilder BuildConsumer(string uri + , Env env, IServiceProvider ioc + ) + { + return ioc.ResolveRedisConsumerChannel() + .WithOptions(o => o with + { + TraceAsParent = TimeSpan.FromMinutes(10), + OriginFilter = MessageOrigin.Original, + AckBehavior = AckBehavior.OnSucceed + }) + .Environment(env) + .Uri(uri); + } +} diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs new file mode 100644 index 00000000..4cca3737 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs @@ -0,0 +1,167 @@ +using Tests.Events.WebTest.Abstractions; + +using EventSourcing.Backbone; +using EventSourcing.Backbone.Building; + +namespace Tests.Events.ConsumerWebTest.Controllers; + +/// +/// Consumer job +/// +/// +/// +/// +public sealed class ConsumerJob : IHostedService, IProductCycleConsumer +{ + private readonly IConsumerSubscribeBuilder _builder; + private CancellationTokenSource? _cancellationTokenSource; + private IConsumerLifetime? _subscription; + + private readonly ILogger _logger; + + #region Ctor + + /// + /// Initializes a new instance. + /// + /// The logger. + /// The consumer builder. + public ConsumerJob( + ILogger logger, + IKeyed consumerBuilderKeyed) + { + if (!consumerBuilderKeyed.TryGet(ProductCycleConstants.URI, out var consumerBuilder)) + throw new EventSourcingException($"Consumer's registration found under the [{ProductCycleConstants.URI}] key."); + _builder = consumerBuilder.WithLogger(logger); + _logger = logger; + logger.LogInformation("Consumer starts listening on: {URI}", ProductCycleConstants.URI); + } + + #endregion Ctor + + #region OnStartAsync + + /// + /// Start Consumer Job. + /// + /// The cancellation token. + Task IHostedService.StartAsync(CancellationToken cancellationToken) + { + _cancellationTokenSource = new CancellationTokenSource(); + var canellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); + _subscription = _builder + .Group(ProductCycleConstants.CONSUMER_GROUP) + .WithCancellation(canellation.Token) + // this extension is generate (if you change the interface use the correlated new generated extension method) + .SubscribeProductCycleConsumer(this); + + return Task.CompletedTask; + } + + #endregion // OnStartAsync + + #region StopAsync + + /// + /// Stops the Consumer Job. + /// + /// The cancellation token. + /// + async Task IHostedService.StopAsync(CancellationToken cancellationToken) + { + _cancellationTokenSource?.CancelSafe(); + await (_subscription?.Completion ?? Task.CompletedTask); + } + + #endregion // StopAsync + + // TODO: enrich telemetry + + async ValueTask IProductCycleConsumer.IdeaAsync(ConsumerMetadata consumerMetadata, string title, string describe) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, "handling {event} [{id}]: {title}", meta.Operation, meta.MessageId, title); + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.PlanedAsync(ConsumerMetadata consumerMetadata, string id, Version version, string doc) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + {doc} + """, meta.Operation, id, version, doc); + + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.ReviewedAsync(ConsumerMetadata consumerMetadata, string id, Version version, string[] notes) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + {notes} + """, meta.Operation, id, version, string.Join("\r\n- ", notes)); + + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.ImplementedAsync(ConsumerMetadata consumerMetadata, string id, Version version) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + """, meta.Operation, id, version); + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.TestedAsync(ConsumerMetadata consumerMetadata, string id, Version version, string[] notes) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + {notes} + """, meta.Operation, id, version, string.Join("\r\n- ", notes)); + + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.DeployedAsync(ConsumerMetadata consumerMetadata, string id, Version version) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, "handling {event} [{id}]: {version}", meta.Operation, id, version); + + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } + + async ValueTask IProductCycleConsumer.RejectedAsync(ConsumerMetadata consumerMetadata, + string id, + System.Version version, + string operation, + NextStage nextStage, + string[] notes) + { + var meta = consumerMetadata.Metadata; + LogLevel level = meta.Environment == "prod" ? LogLevel.Debug : LogLevel.Information; + _logger.Log(level, """ + handling {event} [{id}]: {version} + --- + operation = {operation} + + {notes} + --- + """, meta.Operation, id, version, operation, string.Join("\r\n- ", notes)); + + await consumerMetadata.AckAsync(); // not required on default setting or when configuring the consumer to Ack on success. + } +} + diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs new file mode 100644 index 00000000..2f9da5d8 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs @@ -0,0 +1,68 @@ +using Tests.Events.ConsumerWebTest; +using Tests.Events.WebTest.Abstractions; +using Tests.Events.ConsumerWebTest.Controllers; +using Microsoft.OpenApi.Models; + +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + +// ############### EVENT SOURCING CONFIGURATION STARTS ############################ + +var services = builder.Services; + + +IWebHostEnvironment environment = builder.Environment; +string env = environment.EnvironmentName; + +services.AddOpenTelemetryForEventSourcing(environment); + +services.AddEventSourceRedisConnection(); +services.AddKeyedConsumer(ProductCycleConstants.URI, env); + +services.AddHostedService(); + + +// ############### EVENT SOURCING CONFIGURATION ENDS ############################ + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen( + opt => + { + opt.SupportNonNullableReferenceTypes(); + opt.IgnoreObsoleteProperties(); + opt.IgnoreObsoleteActions(); + opt.DescribeAllParametersInCamelCase(); + + opt.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "Environment setup", + Description = @"

Use the following docker in order to setup the environment

+

docker run -p 6379:6379 -it --rm --name redis-Json redislabs/rejson:latest

+

docker run --name jaeger-otel --rm -it -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest

+", + }); + }); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +var logger = app.Services.GetService>(); +List switches = new(); +switches.Add("Consumer"); +logger.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); + +app.Run(); diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Properties/launchSettings.json b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Properties/launchSettings.json new file mode 100644 index 00000000..b5fe3370 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Properties/launchSettings.json @@ -0,0 +1,48 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5238" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7138;http://localhost:5238" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:33798", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests - Backup.Events.ConsumerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests - Backup.Events.ConsumerWebTest.Service.csproj new file mode 100644 index 00000000..4fc3777d --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests - Backup.Events.ConsumerWebTest.Service.csproj @@ -0,0 +1,30 @@ + + + + 8e0c42af-b506-4d2b-bc6e-0566a6701529 + Linux + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj new file mode 100644 index 00000000..7385f3c0 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj @@ -0,0 +1,40 @@ + + + + 8e0c42af-b506-4d2b-bc6e-0566a6701529 + Linux + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + + + + + + + diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.Development.json b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.json b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/icon.png b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 _logger; + private readonly IProductCycleProducer _producer; + + public ProductCycleProducerController( + ILogger logger, + IKeyed producerKeyed) + { + _logger = logger; + if (!producerKeyed.TryGet(ProductCycleConstants.URI, out var producer)) + throw new EventSourcingException($"Producer's registration found under the [{ProductCycleConstants.URI}] key."); + _producer = producer; + } + + ///

+ /// Post order state. + /// + /// The payload. + /// + [HttpPost("idea")] + [ProducesResponseType(StatusCodes.Status201Created)] + //[AllowAnonymous] + public async Task IdeaAsync(Idea payload) + { + var (title, describe) = payload; + _logger.LogDebug("Sending idea event"); + EventKey key = await _producer.IdeaAsync(title, describe); + return key; + } + + /// + /// Post packing state. + /// + /// The payload. + /// + [HttpPost("plan")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task PlanAsync([FromBody] Plan payload) + { + var (id_, describe) = payload; + var (id, version) = id_; + _logger.LogDebug("Sending plan event"); + EventKey key = await _producer.PlanedAsync(id, version, describe); + return key; + } + + /// + /// Post on-delivery state. + /// + /// The payload. + /// + [HttpPost("review")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task ReviewAsync([FromBody] Review payload) + { + var (id_, notes) = payload; + var (id, version) = id_; + + _logger.LogDebug("Sending review event"); + EventKey key = await _producer.ReviewedAsync(id, version, notes); + return key; + } + + /// + /// Post on-received state. + /// + /// The payload. + /// + [HttpPost("implement")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task ImplementAsync([FromBody] Id payload) + { + var (id, version) = payload; + + _logger.LogDebug("Sending implement event"); + EventKey key = await _producer.ImplementedAsync(id, version); + return key; + } + + /// + /// Post on-received state. + /// + /// The payload. + /// + [HttpPost("test")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task TestAsync([FromBody] Test payload) + { + var (id_, notes) = payload; + var (id, version) = id_; + + _logger.LogDebug("Sending test event"); + EventKey key = await _producer.TestedAsync(id, version, notes); + return key; + } + + /// + /// Post on-received state. + /// + /// The payload. + /// + [HttpPost("Deploy")] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task DeployAsync([FromBody] Id payload) + { + var (id, version) = payload; + + _logger.LogDebug("Sending deploy event"); + EventKey key = await _producer.DeployedAsync(id, version); + return key; + } +} \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Dockerfile b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Dockerfile new file mode 100644 index 00000000..5ed32258 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Dockerfile @@ -0,0 +1,22 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /src +COPY ["Tests.Events.ProducerWebTest/Tests.Events.ProducerWebTest.csproj", "Tests.Events.ProducerWebTest/"] +RUN dotnet restore "Tests.Events.ProducerWebTest/Tests.Events.ProducerWebTest.csproj" +COPY . . +WORKDIR "/src/Tests.Events.ProducerWebTest" +RUN dotnet build "Tests.Events.ProducerWebTest.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "Tests.Events.ProducerWebTest.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Tests.Events.ProducerWebTest.dll"] \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Id.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Id.cs new file mode 100644 index 00000000..01f24bdd --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Id.cs @@ -0,0 +1,3 @@ +namespace Tests.Events.ProducerWebTest.Service.Entities; + +public record Id(string id, Version version); \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs new file mode 100644 index 00000000..9ed1c253 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs @@ -0,0 +1,3 @@ +namespace Tests.Events.ProducerWebTest.Service.Entities; + +public record Idea (string title, string describe); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Plan.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Plan.cs new file mode 100644 index 00000000..1c735c79 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Plan.cs @@ -0,0 +1,3 @@ +namespace Tests.Events.ProducerWebTest.Service.Entities; + +public record Plan(Id id, string describe); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs new file mode 100644 index 00000000..4a574730 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs @@ -0,0 +1,5 @@ +using Tests.Events.WebTest.Abstractions; + +namespace Tests.Events.ProducerWebTest.Service.Entities; + +public record Review(Id id, params string[] notes); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs new file mode 100644 index 00000000..4a2bc442 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs @@ -0,0 +1,5 @@ +using Tests.Events.WebTest.Abstractions; + +namespace Tests.Events.ProducerWebTest.Service.Entities; + +public record Test(Id id, params string[] notes); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs new file mode 100644 index 00000000..6df08a56 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs @@ -0,0 +1,70 @@ +using EventSourcing.Backbone; +using Tests.Events.WebTest.Abstractions; + +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 + + +namespace Tests.Events.ProducerWebTest; + +/// +/// DI Extensions for ASP.NET Core +/// +public static class ProductCycleProducerExtensions +{ + /// + /// Register a producer. + /// + /// The services. + /// The URI. + /// The environment. + /// + public static IServiceCollection AddProductCycleProducer + ( + this IServiceCollection services, + string uri, + Env env) + { + services.AddSingleton(ioc => + { + return BuildProducer(uri, env, ioc + ); + }); + + return services; + } + + /// + /// Register a producer when the URI of the service used as the registration's key. + /// See: https://medium.com/weknow-network/keyed-dependency-injection-using-net-630bd73d3672. + /// + /// The services. + /// The URI. + /// The environment. + /// + public static IServiceCollection AddKeyedProductCycleProducer + ( + this IServiceCollection services, + string uri, + Env env) + { + services.AddKeyedSingleton(ioc => + { + return BuildProducer(uri, env, ioc + ); + }, uri); + + return services; + } + + private static IProductCycleProducer BuildProducer(string uri, Env env, IServiceProvider ioc + ) + { + ILogger logger = ioc.GetService>() ?? throw new EventSourcingException("Logger is missing"); + IProductCycleProducer producer = ioc.ResolveRedisProducerChannel() + .Environment(env) + .Uri(uri) + .WithLogger(logger) + .BuildProductCycleProducer(); + return producer; + } +} diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs new file mode 100644 index 00000000..310bbf96 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs @@ -0,0 +1,67 @@ +using Tests.Events.ProducerWebTest; +using Tests.Events.WebTest.Abstractions; +using Tests.Events.ProducerWebTest.Controllers; +using Microsoft.OpenApi.Models; + +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + +// ############### EVENT SOURCING CONFIGURATION STARTS ############################ + +var services = builder.Services; + + +IWebHostEnvironment environment = builder.Environment; +string env = environment.EnvironmentName; + +services.AddOpenTelemetryForEventSourcing(environment); + +services.AddEventSourceRedisConnection(); +services.AddKeyedProductCycleProducer(ProductCycleConstants.URI, env); + + + +// ############### EVENT SOURCING CONFIGURATION ENDS ############################ + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen( + opt => + { + opt.SupportNonNullableReferenceTypes(); + opt.IgnoreObsoleteProperties(); + opt.IgnoreObsoleteActions(); + opt.DescribeAllParametersInCamelCase(); + + opt.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "Environment setup", + Description = @"

Use the following docker in order to setup the environment

+

docker run -p 6379:6379 -it --rm --name redis-Json redislabs/rejson:latest

+

docker run --name jaeger-otel --rm -it -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest

+", + }); + }); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +var logger = app.Services.GetService>(); +List switches = new(); +switches.Add("Producer"); +logger.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); + +app.Run(); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Properties/launchSettings.json b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Properties/launchSettings.json new file mode 100644 index 00000000..76a84d7d --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Properties/launchSettings.json @@ -0,0 +1,48 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5132" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7063;http://localhost:5132" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:48432", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj new file mode 100644 index 00000000..830a0057 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj @@ -0,0 +1,43 @@ + + + + 8c43cc25-10b6-412b-9772-94e1a069889a + Linux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + + + + + + + diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.Development.json b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.json b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/icon.png b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< zQ`O5*Edlaeq0vi>ja?8CX1h|mLa0-~Z`C|Ff*aOE16d!l5>(b_iPz7GBRnFF))$G} zuPmX104WR(2?*2{*49L^1RnQ4v)Vt5UGpIeRKhd_%V<4PseJ0@1$=zqMt4SSH7Pw{ zk48(0mpR6&*Mnb=6;~6C9ih$hVhDUxQKw1)f$*tx3y(K#+ zdU|&+Jr!*x^o*jGjKkiIu6h+amS%imK55w#uWR&}P{NwDv~-*P7R%#P_fPFA$s{U` zFg7g>U)Hv~wK_E6jhEwpU6%HOEG<;Q#rP*SP&|^7;pI&~0po^b?DgHL3(l^t3#VPqzU9Bv47}?V@aS`B5EL4UpVed`fXY{PWE(zb zxl5(KIPDB`fH301k<_!I3`atHkcEpX#7|V(IvQ{u#RN2L>KG>BK5@cqj6io$Y2)kI z4-Lds_4jB{BfU_FZZu&-_KIYJmNw%e#5*c3#2serpx_@TalhKC9QpfO??M)jU8rKCr_fpA*~AAQPKdLhUU-TzoVE$mE0G@H8hUt)ygK3i(>nzxsoTcvNNV8r{rS*!EBkOAeKmj#Z#8 z)qb5#x@D~YKqVzPjeIOdZjwi5N>c)RQTW|8ISM?8S#GVZIo`G}x!X2g@Iq}SRXT?1 z8F?VoRo8iKsc33)zxI(&$&!$eZhYEv>UANMT`x!zMo2&k1NhvDl0i8!v6orOVcQn> z`dCHp&>_j^=H_xj3BdJ0Rrfq2-O}7Rx`Y6_6s<#*9)$JEjPK2%WcgjC1}W~N%uF+i z*L_6&AxX2W@>W6Mn$L!aqbfI#pp{z>-n;t&|6N4wZ;N~#P>}*h( zM0|Yqo{}@>-I-*edfIBr7NJ>A4>iZuTp$DV^(5Q#Je=a{u%WS% z*vK*+S`eA0HNR({qkBSh2bdk!Z&am(@-=m%>SeLvB{Deiq-7oE?h5a_qtaj2T*HMb z?18wSMyl3DXFQ<3+_plOvK|5^sB~U(&7tJwyHH|OswbP!?xJ>A83r+>$ji>AgT&ep zjuk~zyeKKihpcm{enOxUWZ!Gte@#ac*=1*K{qPwj1JaltE6c;f1E$&i{ymRWn>Hrv z6eB~B_o(fb@`d^%S5CJ#_)1xU(*TK6@S5L3Z%R07I0zHSz#F^QjfpHR^$H@u<(poM zA;vheY+J!kseoilyw7Q+5z7Dc1(%64N*4&c7ax0Xf8Q69nfG~}X0v2;u(VNCfk2*l z!NuvyUn9T)HU(>)^5kTumX=JE{$M?bzCG#Erby&`mt-K?*N4ghX=NjAP|UXz1T`$& z+zF`Pdq^C2uB0B%NHA!-@ci>J_*IQQj=;%A%Q9f?RJ78MKl?e5Ecrr7F4XKW=v68S zuMWqY29&@8_vGclmV`RdL_seuvy!iOy=M(w;+j3`*! z-sK!9m_UU|AaC3F4wUazC106*TxSNgNZXMT*RNlO6v?~(ev&h?vSc}^GAw4Fd0F@s zo+v2_6`jCSTTs*k75rTTc=uBZW9z|aLY*pv^I3uqF-(r4V8YkY!LlGeF@Sn+)GuH_ z{Vr~V#OWbNP@<|pnWtpPp!DIJKL44Jl<^g9|1R*!c z`Bo$?B_vh6SG>m2A!q5UqRG*|xZzoPKEA_DR<=6yXr${kWlUDOBg>LC<%y`^h^7aQ zAqrHeL3)-~EJSNz!PZ90!0-X0<7Tsx?(Xi@5SLc?%`9L6s)AR6DZHGN@_Hk^6+Jd) zu+;9-9LJ5>-k>}|Vks@_v10i6)zjxYqbpExqo}JpOie{kcC<)~K{Kf*Dk&-KQoJS$07OK4Xc4aEtU2nk_? z<3xr7!mv|{wBBT@kG<*WXaZ_qvq_o(?&U02(3hZh1}5))zOfi32+ff-s#mI%RQv=1-!1_>S6sXZ%Yxbg!tqGEx1}AT3BPV0g`N%tX z)30RkA^8+_;AdG{pJo;gz;d6}1+%y3FMw2uW!V~#R_`ZYf=a`e?V%*)Y;U#u`LJt(wmz1S9(_{4d~GvZ1XkP=Nxb9$ z$#4mw<-U72ac=@gEX#TXoF9Je>^uFF`+r!v4sfdXHy#QhBuP_>I95`L6r#Z~J2Fzr zUP%#3nbAO{%ut!fo*^P5l8|IpMnn=qkwoPG`F8*JdG2$c=icsd{C?l@zMu6*z8!{{ zj{P^^cgFLpnN}R;Vz)cAWhLZJ?d|R8fpSseLwnOc)B4;}2Fv1k?Be`Twfv_CofAdI zS(MO|qwAtIx4B-Eo{f^S;9uvzd*po5#3_}V`97fqt4hnuAK`S8dit3MJrFu!hu-ps z=#26&3@Ljz;j-JeNvCAVZyGbt<_fO)@$>f#{z)P-v`(yFx9->(joFQJQw%S4LZU-l zTpu6;1h9t_&#cgR+Og?j>v^GR>2&{YhSAZ$ z^wcc@nPqD!{uw6(zwNvZ5ojXkk>a>r0&;o}cX)aN3Umfxl-yHXP!NVQ?FRWfJ6r1S z!i4wKWW)A+K(gRq0iMl0(FyhSthDjj!}V@9uyz1)5VNALC>bj@NHYGj`?)3^VwgJC zt7{mMDDvjumU}HXRj6xa4llm)xp!l?vy)h#!8R?l0V1&;GOI+iyC+yk2VQSjUbRYR z-*dFSb>0i_yum-bhM$S%9K3`fbIBW}?t=nA{w5}B;TnVV%F!`5a} zP3(GxF70!~?Qgbmw)qSG1!l9`)X49wiq@jN z>w{`uH8xtkyyY7??^O{Uu)q77PrxdX$#3~hlOScoag(O}Iorf4=saEPB=^Qlly}RX zJs0n$51Ad#n+$%Ec0GCCRT+UqY`IpsW`23MwOS6J@kb}{Tp1u6y<2p%J}5$z9wg`iY%jS~~H z&i?@AF(eJU)oIU7F80*GtC*UTlai32vi@cT$0dHF2uB$Kd+`k5voBx1JdpK?t5xoz z==dX*?|DWJx{~ogTcO6;k@?vMevb3YSA4sX>s%-mAF?6+;Yn&pqPYnGt-^Q}R6bDH z!W@P6PH*x7=nCS9L^BMz&3?+l%)Mg0+5sm}k+=hn^m~+qaG7rY`Y1Fs@sY;a{A1|K zcdct@>0x?#7w3$!by(6;UGp8-k9E!^`;V|lA>~nb^jU#!>#_%Iy~m1bOuuvf&BgNS2+0$d0KTQB zr?+T#_A=@)I++URemOt?%61oaOgz-15H{h+IZ30&m>&@4S-#2h=TRto&{xX&}v%YIsUvT-8IA}-UW_EYX-EHT@2=ak|U{a9FU{OY76O@mdH1}xfs z+u)5Wj;`To3A!eKVlQfx>hsI#VPI0APQ0;ky_kdq%&U{IF+k;P&8?c3jN9~jLlR9CsFUn#`tBzRtk2kHQVgP;Tcv^Un>I79C_jx}2l#kixDH{bnr zJ#7Bgg#wNZ7U6FzR|>!&=$xfsi}m;PWVJI(xWxEqcIh3Qdv{S|p!G-hdN!Ee_|>3o zQ-Krrwf8O&R*K&@QU-N6rzdXL4%q9pRyV(86z#}n7F#?5A^J1hoV=D5#!RI<=1?r3 zta^Pv>7g=T&@xa){mGWQ8mUyGO0OgI2GD(oPlUiq85kUfQid;xUSR!Bg)*maz>BJB z+IjZ{u6OcpZ5{}ie@@nD21V`XKQD9g!i|3)eRu8Z%T?6+dCnkREFt1)*uWVY6Vm{A zRSnKrq7U__pDT}Rd#g@4Ug{c-bgoF%P`vh99`Js6&;R!a7gS7v;Sujb{q& z&SR?vzN^pUI!&%KOt)0qmu=!?*Psro&&-8U^gt9;P0VEUW>|g#?wZAJx8r3ha9tUH z7oXgZlj?f851jhX3~UEF!EYpg>?D<*dE|_~E&H`GHw9>#fLnP=)OyHy<*F3a{{a8Y z)%^G2y2+!G?O@c*^Ni{(4P`!sw*?Df+i%WnzOj|R+MY4}(<1dy%iSZLr5SMhIu?Ph zAWjiNZvnc6>^FVZ=((w`$!fiBEqYQd1fIdQq&p>6TV7tqhCH=e}+aa`u{uV@;$_6w#@GUE_RmorPj_Y#0&w~3HD7we7rBg zFmYpk`f-U{CDopb?ZEpnxAt@tr2r|Kv5maEy!LL{|5u zXvn%4+5e-M`n_`|Okd^pdWWRlq|jA*pEhqH69t1}`l-_Xs;4KV&>4`$4L-la5a6jz ztfwr2GiGO-ytPqT&(6-G+u+r5J$uhZf={of3bYtHpn)GyW&K>$_q6(lMYx-3;Ckz# zuDYhC6dVyRUmnI0cD+YNf{E>Ck_i1Xm?03v^1$~}o@GB+v*Ph>K|3H#4W_ z)zMLkP-1Atx-a&60@d^WRI}!vxEFf$@IT|F!2m{lwtM{u0v*a#d;9kqrk0&YIH)Th z{{z(t+`ey9Q=H2!vvF}9LCRn}cQ3iOfZv1jpi-KSmS~F%*sebOBOii!^{tSav$ofe}4nyRHG1$S7T!%%9zdmeIIOM2eXPknq#dJ)Dc=Jv|+^kv1*&9 zY5$7=Qp4gLchS;N@a2n(^8Laa%c3}A_mEX^sju=9TvE`x&t&JGukLyDcfe5osnSy`p15It&v1BkHO>xd6k?aS-lTAts!#eJ;!$8kPFDN+Ho5J09=blHTLBFvI zD|p;`Xji{qF8h-&@jz&m(zGQ#Gx`)L_y7A1rWGJ81>^*Zz{8Q}d)~jJ`R>|{l{V^Z zUn#DRaBx@jHUSwwo@c&{^M83>_>kMWs4WAU1-!ncA)Eu<#`uv&T#~t z|2F{v>AMH#lAX)Y(g5s1>FUqDOI8+Umw-lVSgB1U_Z*dALj(t|AMmF3Z^Aj1n8)9) zQ4O}#5a(qBa)-BXHP%^((pWYLzy zSC(@8Y`-F5=ajap2$tsjH+zGiK_xt0*r{tES@D|zSezyNFVuR|c~M-x4Zdd+$NWWC zFTsBX%1cmgToWbxB#R#1ENt>rj2*GI*AtN73sU^9*vNmKw9kr{eIj|QXr1qL?o0b( z{T?ag2@6?8*Vd&BxkISDd?}-GRUqRKm2%?n=tu}{&9GZ(T2_|6y2i|v2Xh9eI)|Wv zh%YMILm!KLOf%Bgr#srnL|Lm*F8F?ozf+f61QJ4KGY9Ha&p*G=MX`j( zqU44842ys=?c{TsI=p@?RI{#i^3uN759C9^$QucqTr)RTSn(9zdN}o@!7Ag=x4o9fX111hD-Iotm7QH*h{OW6qpXfF11n~;1(trjP5vUbBG4?ZH$3Go3~>jl1Dnje{0|O9Gz+mCnAQx> zGgHkjXL)CG5ncj$Q}f%makf74yieCH{Hi}=9AJ6KTLOS56t=c?Y|JIgSFGa#0RZP6 zt{2uD)my9TkMbGbd16UJ_ub^bPeXwp#0D%Y&+j_k>=);toBI-)!=F(CYSWjIm}n? zbdWHduOlN>*7-XC*x&dy-O@sXAswz5{ps>kptBgarwx2@Ag?9y#H598!=?tbx33vQM3kWnP> z)474G2+qsFydCqV)wLKS+-O*f-d|QQ71|2p)@s3 z)mN=!Zly~1)Fha^mfqT9O>#hVON-}~%Z#?Ig6tHX`AxzGOFGhbcG_?cPJ9Le-Hz|)}5zNDt zdyw_V1;H2EeJf0_?l50bHK$YbHTg`>b0dlCJ&r@KcQ$qBthdfHLS>1oTvflR&9s`e z-BgBYB2L`zD2enI+`!!~8t6yzqneWN>SOe`fJ zu5O)E-90^8aAS=ihJnFt$)eRQcIM(E3tAL6U*1DFfz4Z9E= zKg8&JUJ18#tgw$2UxYXwj93lYGMbv!^1h2MF1mVpiQ7xcx39hUr+kMCc^1_N`|Pt^ zmoar}@daH|6O#En62@~L5c-QjXS?3H`wYv!+`FE^&zha_0*f2s@K2B$o8u0GJ{K^7 z{uW;6m0#-=v|@5I5G@*-2EWf8^}XmvNhm zf48w^9Wj`{UchW&6?t;#d<<_Q^PQ7a#DayQGgJ^#j&_ zAoLK(xCtn+Y+{*znYqKF_GZQ^3rOQgi{=-yp*6a(&gTzAhd+O0NSU^?&X*0H zSx$_Lu+9XdfQD!-a`MXR5AgHFi)FnWa6;{Z@)6fJRv;=pbSTgX=mnOC$CZApYWbVf zG}Pi%Bx{%fpa=k6gp^6NW6duY3;y+R<^*c|UUqheBl-=ZHvpQi%C}cs_i<57bo9@w zp6mHvu?nd3Gqs87;D-RNBhEKT1)9WWtHSNvtO)8NsaUP+!x(L{+CRJOh1Vrr_RrsL zm4#6RyL|lkv0y^{5^U1IqDWT&Nl(@xo6k0D{ilaZ{f{is^PLKVn8P1uH%Ng<^`jTK zALr+j;s+0#o|&onr=x4@_W}c%yKmxn3?L9gaZDLzhuR!ES}g816^m=fURz%X?7aGz zC)Go$=l0<3cCO;@j~K3r@;c;HP+#llD))%Ba9O+*wL>Axgmi|YzO3t z1rruJ6rmhflmxuzci;+5S2-~G``xChfqlnHscCEBIJD<00i4oxm%r9Li zJmH*xpbGI3x>+~YPhO4IguN9+4oJzbKVR<&uC#2H8iQ&?9jOh3|3hZU$He%%UFVk{iO?ukX1$lO57io^=GWm# zOY^gr^Dl%W4yVIUwiR3T7t>c=;*!&suN4*+hIeB_aBVe#U|^OpZr2t5#S79id40X= zi7A_+uIXWBRpaBv{gbmE#6phR#!9Vmhos$eAD3w?GKfq%74c*d8WrjlZut&1y7d?F z+uQ%quJ=u)Z^CJ|J`le3qbEtK*M?imE+)He!IpA@)kFqnkAe>!cH2{Hs{V6!5hrr5 z@s6wBb6eTWC>77g+|V615yAj2J}3f?sjEAcZs5ETeU85LF&gjC^{4X)Lrts-!1 zU#W9GX~PJX*?zxM_h8Ch6PPt1WPDZl!2x+i;SS;NyVY8a--SGbC_hT$(tW3^iu{-7 zhEp==+o~7-K2*4vT#!lapI+%0bFOZWd$V^`!m;=h zGj?~_Sw0v8&EtI4+W7l{(rd}7E6hTXF9))c$Uq=8s@@_2wI@ngD8xEMZaGq41|F7? zWlEvvFAbMH#m*;Tipx8FFA35E%u-_1DYIU<@@%2;@x1=UXJ(!(8$G2>{urjMbvSo! z2ngt5*5|lD;=yVgV1f7HnbZusIJau;aYCF!Mt`8nP4~Jf?+r|i=gHB1tZ&0ul5=T~ z$CsV4@^Krn!igzOLnG)^yxD@NJj=Ny`jw|cJ}*Nd7y%Z*CwW1ybOGP0 z6_DPybF`K!lh?|Hz6_|dch~#w3=;wMjkEd&>Vwwi<{jVo!Cl6Ii2`$p_OkHY*^P&W z5;heZ3Q7nrg*+AZx@GuoZ@ar6JeEl*zUo%GUzm^14e7Q)y}eyYzum&<@MV5xzXLd= zX8YGvsF|4wLMO>d(i4K#Y=1d1^DivU0Zk=|AdoETP7uj@@cH&Y{w>WK4(ub2^&&gp zPH^II#KVA_Wdly~YBv^r>&}vJT*02|^&{0zZ*PdpN%-rAD}*nZf9B3)@`lqg4fP<5 z%+r^D`8`>SWG|DK5L^M4H-hawcJ{Rm${V0OMD^gjS@=^yLc&$zM(=Vg)zUClqeL(TKSZ@l zv@4ZcZ{w;($_wHF_V=xp^sHA&E~(ONnqCyQV+ZyGo;vlNTF!UmvK)lMwW!;f9i8)K z_h@gX(|fGL4jA27eZ%hR7isRh`UAVMm~`9OuCvpVgte|;+4@#GPa~PY~SuzZ-(%(thkA`d~#AD@ur*l%eddoOmm}Q_nOs%3QN67f1 zHY8#IJjb=dM_X|4Aef3sj| z`Tfd}zZl6N{59|+*)(b=tEzq6J1;e&=$HHT-a`LVG1GILr7Xe*REomkW{P3}!$yi} z+`|qi+g{-haenm>U0@|3>ot~T)RAgmo@QPDhi}9h-n>c9&b~7}`Yxrg&!zWDblBup zQ^D(A5wP%fS4AfXXOb?MMsrqPb!_c#e39XL-E~J+y2mEJ*-pPeH)fZjd4TlZp3O6<|djs8BEEm4|C4nkwG)dsx2O=dON}f z_P+?e)~Wt&Y8D{lVPm?!)m8Dm$H_*40d_eW0%?>LVid#}MIf>D&xqK7?u(f5`voNv zbL*;I{1bZ&@tQ!u6CIAlGYuVs=ox3YH9VtA2>P_!`{>n1?+*nH0(xLW< z`uvE)!YocoH)HlgP7J-5M z{}^w%Czty96~sxXbv~W{+Las}amGPq>xzvW%xRgKmawxU!jo&u4Pb6Wv0gsco`Yoq z12wub^7XzK=6K!?2?;6Si~1PCC<@XHPD7L-h<6#ChN6eL=C#bz8>-X0zJ2{_3NJ5# zaQp8a*|b)OF|{}NV*(SK50~$<%JJ&76VvJj8tC1)Hs^d^GV!hgk59>V%Qr4SyNG(i zBY+a`M!I0Q{}tW`Awm4MGs`%Vc0RZ~k_M^v4q>m=!L@1W>HUcC7(y?X0BR7I5b&4= zRj&O9aeW;gRllMKC=zWct|iC;Hv&8(iH$I30kMMilwMfqTG3i>U6_l=ftcr7(UT8Y1Bt*%f~;qkS0u z`nB)Y&NqFi5Z5~|b{3zK;H!p@1zr*~BJLnLXirRlyDH5!2NU$0v~?>-H7-hBM@-e# zVL$^!s(~Jhm;tW4unfJ9s$Ok)@;Q-o=OZP=D&{0*Fot8-gXsm4ruAFDdVRjfb#h7f zvhEzut$1}fk4TmSj@Rp2%+v%+$k&=}k1565AGUo>RbbY6$3t$Oe4D0CNw3if)(|`z z3Vg7!mI!6Bs}sAY#*H+L2?1%-LZMB!57)u116o19jyi0gXmkXbG$O>TKl!;1IXSnq z3|pVxzdWP2k^u$|bXazGU6Z*cb>QH|HA%klPPcDHV^{PmTW_TOCV_K)=cT@jy%qMq z#PeG=`k*?$XpS9>`XDjZD)4bx4Fb7K)(A+Id$6CC>v{UXmNEr7aJ@ha!uvU?6oBD_ z7h?ZxAZBLV`d=WG52ZSzVM6!x_TxuwoSUd9$<4Sl8J4miP=QQAOO>37X` zujj-J(%TwU2bFf+M2f4HY*1V`IeoDB2qq0p5qsY4H?Rat^R#Cl;#Cd zcsHMFYW3&#mgk5lM+Ua8Qxtct+50fEii;0`sv3l!mw3=0J$hueA{~(>gv5OF zW*c9ST_~T;t?#DaK6KW>)FwZi6bc|sr$XVIXb+l4B90M?R!X>_D;-U3Qa`9Fvrw2q{i>LZ>#~p z*kx(J5(ppw%pPK=dnds5o5{wo4940?>I|L~eGy;iAR~NRY=Xt_09>)J(Gs*dF^l1>ya`5>3Uo8j-#HEJLX27m#+ z$&=rmy+6bZ-s(n;4%@S~KD`LW64sLQus?$o)*n5a+AS7Zmn7S$dz1QM5jWi%5>Tmc zeY|S{yfG?WO_Xi;#m=(TsZ%CcCOMF0BXWDW2Yc}eB{SHCpx#GC|3+?rLwoPA5TDe^ zSGWlEMiv3Wl5jVK#(qKq)kHhK&6*O`vNI>6o!;du^UiJJo?Wy#$-MT10 zwsc}_%nDaQ7y<|>D?Hz6ULUO{ej}d?{Z(-Oy%XqZX`1hNdPL4O_#q4lHf(scHzw1lXl=!m z%kG03NnuLX{AJ6P>XsU%tn2;2CEvzqBUbxBP3Zfjed&9vwH*C@kO^?%Uj%cp7XuSDxI0% znTo%_!N|codr8;$*B`%-%efUa-m)`KDwdzp)5DV1vZ>=to5{Yor}b>8wizQckN^Hn z!JVlG^DA1fs~RMhxn~?Oimj|!sd9{6uspLc4)RK+#n~o(L9A`X=;&zFxY*MF;yBzZ zq>fAQB?2@ih&a;OHq$LZcb)WJn0`O@>p)h<&!netRuiNJI)Tvq`H4TF;!Z3e?-ks$QkMM=&eeZgS$s z5ryLbb?bg;DT^M=Ln`7Mr9}_LKOY#F67cr%!u`4NW0{;?0dJhjSwj(4`tzX)Zw@Wd z4>%g0uZ>;@!T+wNnMcOAe{17aGV6I2sdAsQnV z2Yst`wm@+_Mo;vb!(a`~4PNo@4^3Xz!+35~-*fx$5sfC$2QVx;`+h7X{3bweio25f zNp=?o!wvCMZcly%v;_VIHH>u9sa%ivkHW7%m)P}^);TqmHL_v%*LBFkq7&;z=he@* z3>tN%OiN-Kbs4UV$%5;KHgL>(Q^>{_!Pfjy<1E2BqKjacplx_}^1=$~&9`Ij9OR71 z1se$a+W^uJg@+zpLBh>V{NoM;t|a!g1)Z>gK}5Q79ET$Uhl#ZrO$NLR{oE zZo`A`i2U=JiHo5^?XTx*qhH7NLuwiS$oQFR&Cug+wyQ<&|F5e}&M_BV#rP?SD%iQ(w_w|ZnvNcm) zUe%=huKyMHj<63Ztc9ERakEMTJx5MdQabMjd(r;uY`Az(;0nUG)!7-X!VNBd9wt?T zc1?O10A?E8vdM=b48h&s%5!>KhUBUP)+5oOlvg=Dr;baum7R7gc$v*nHD@Vz9D(e{PrGEr^6^}M_kVFlpyMpkOuu0Q}YlYRp8OETKiJI->ZoYCH! zAqst-%fzkfBd?+iL}u3fe0Snb-PxDp6Bi&?7qJ^#j z673m+)aH>3@g+IN5o}8<%&HeJ?!j?^b&A9rXo=2_yzT6yf*B#Tz|)^C8t%V7*tnx$ zP0PsGx`7iQJwC=L%K(Q{^xO0m@4acxM@KfTkvge%@#2h`ptz(N?v3zV2!(L@kpdWL zLil8GTM%@%dv2iC9ndj1p2>#X5H;sb@>A>^WKsK)9+{HzfHV%*Si(<|a~8(4n&s1T zCd1tthqTe7i9gcJf;?4f+219Z&G#OzyM zPhWaDcS48jE**qiYNU+Oy z`=O598Kqh9D+qQGzY%5d zE9@ORJ3EOK_r_M!`^dGFu^R!g9#|=aA&3W}g5?WV#)w27yN#Jcnfbu`@2uEbt9t); zd^GW)`<7CslSYfouX$Bo_;X*tXdre$Z4Ey4Gs|;X-gACiYOrKNdh=d8?v7?giq&my z^T%$R6&`9sM`dSg8;4;hK{W~5-7p{G-{@aZdKiXi>dkxh7={a|r?`iYb7r)0%y}qc zsLozAaPDAl0HSnX9wqsHD9+kK1Zp0mf`HBy?;Fb)jUKiEELJd)k=BIF>DaXn+7NEm zrER-b6W|hlh8JK6$u>ev&zpDf?Y$=5>!Le%%Qe)f9s(qh$!**q)A8_#VFn#LK_GNR z!(@!W+o5#+FBwmd`4oysslF#8B`AU1#J zXVDs-(RF(4R&_=)5XwH+is4K_$yrgUP-vk_OBJmCV2aes*SuTWL)*?DGn}r?c;M$^ znIXLK?4He8fx|}xJYN7nxK(4WuCCL(##G!O>(rEHev_V$URaZT6I~|R()kFD8Xu5$ zdyUG;?eu4gcAoo}K4b2#Q~or+=69Iu=M`0Za}_SFkTy^_%FBLODHZOHmlc1fx03*U zoWh(~I5f5^s{)qdF_J74@$BNOID#9X@ko-0s#ZR(j`aa8lO>=Dd$9DuAnwb573M%g zhm^%DC;>^DmdC*Q-ag#uW{`T||AegI8hR$EXoBnI-yjD>WjQv z6;=SLszN;KS2XJvaW{ZOeQejd4e2ia2R@9$#vzcqiTAyBWZ+$??^6Di!!9i-vmjU5 zY32S_x`1V!$1jC?-K}3 zlITs3reWmQzl$8;ZWCH7Dst^m%x~uBn8Y%>@X>uJP3oQ(Bb>iKh$jgf5FA0O@i8_* zyMR_%MJ_V@^t$`CuDtyfc3WddnQ{@B3nJ?T5-g&*+-)sWpA z8el4|Mg&iX|DvF=2B3u_mdNy$GNPV=@nGbtn0OklNs=k}|I~;yd5zBZiTy9_)+ulV z9w%pG>2ZL@Xd+xUSlUzX1-uT^H}!mLqN7rOrjt`yeHWvR8b#pUi5vB^80kX>MIh<| zg<2HQpdWZ*XA#*}2Gn{{m%6qiLU7nSgWLQTcfV)fdSo2!eY>Q9@k8M8@#)Lhw~6WL zY>+Ks z$Gv_g3Sfcjom`eBPGaFnGk{n18)h$p_iCVOBr{ZCTmatcjDr}dEm9<>6Lm3lGq!kD|XUyJXm)Dd)^MqQ6pl?zY^Uj4Y4zmltLb^>mZ;)n&k7`Xw z*qBA${gRPL%K3^4-sOYIakuH!3@Q@(l$2 z6N6-lqB)Q9ik#CGUVaUOCEb=X?HAqL9x=}=#TguVGJsqKj;eJVFYQ72wYnfX&@DeM zMKwrE(D6fjGWAxK3r|dbFieju=zFyEGzg>p;7dfu{K_JD|4U%adL*fa6l~(+dJ`ic zYt2O`4Si$i}&rQSshwm5lBU|c>SV`vcmfFvkKQCR?bCaGCq-O%XY zoA4M?K!x}~J0-d(9IG41JV{_d>kE992T2c43PU};Vy$>REPqf;|Es(a-wR7$Bv8w2MX`e!^R7=%g8m`A zZa#=8(uCut1(WN?s^PSJ^r4UIuzBddLJClVt=up>&A3|78v#pC- z0`2eN#G_+pyc*rKXk@UN2xX4}n!1~P{@j69d)tRyKHkHkwXz!Ah^MSGGc%LZ`^^1< z>bY|{k8;O(RrWb@cYLk1?pwzEvzejI^f#HCSa<@11rp)tT<=(XV8vfVe1sRk^YP*7 z1reyO2+{@NaoRdV>PIMBmXm`Wh_}aV!|5-Cq^OxKaEuB$95>&C+%n91Awzmdj3_Z> zpHx3K}lFX4z%gg0xqP~BRUK^Qj$j&yYS5h*K4xT znR?5k608vZ;08ia`8+_E#Kc64yN8lvU*K{^EDFgxhjI+voRS{nV*NS8$=49V6QT;U zIr{)S_hX?TR47CKsPT-(`nOG%KSUXv$`q0>3M4j*q7Qn2;fwnwUv0Gu*seEVunb1i zq|{Wk+ZLR{M-uZwcwI(W8#6bY!S(|ge*Acvi2U?9AZ^clnjiyi5)TxJKKn6*_Q*1m z=T3G3vro!3ZuoSM(p|(2qOHVMr4|(#y6y&(fc_YFAeu_CLxht;H_5zAC*0Q_NePmY zgGlx*q#ASvq~E4cUL-s`2p@;F~dPIfyi1Gv^sbwRgfs}q2Sb}He zXE6S=JnaE+r_vqQ!mUYcUHF+F1(R>vqo$hIJ#HiG@ ztH`#zskgOt0Kv5dBRwawYL0`?DMc-5R9sq{Qb^b~!l#aI2qiC!!jMXwhElK3tIjN5o??H6_l*XUH^%9p<~tu6*FciI|b{jkZk8 z^eN0fG?Hbxx{J#6Xa5sO+S_GXcy1@8(TiQFJy!S_w^Ea`ZN`w)On{5&|={NaN-3O{5v92(z?e!HuyA>8m3!eN#Z3UmZDz7MN zuPcU+Nv;h_oKB)^`7%Vi9xOMQ`nVO%&;_6qYp~JjNvgJW#2|wKC&b9G>|@T(yRqRv z4uhX(^|w~J;lt(Q#O6dq`Cx!Cy@W~yw2cI~kKT)(rpkZpaB6oW{f@ZGFjN7j+xxOB zdjR`TmPgjxN6JKneVatx2Z`$C$l$|?OBsC+_;q3epS)oS10O>_N87riWuD6F3uBB>iTZK1}o{5TL2pNBfsl;Fjr@ap|PnAUjQcDXh%gNCx+{R05E{ZHNk`Qyk z_=6rY@&f{Ewf#fT5x2V-s!=e00Qu!;JEIgXN|ET|iHb!<{N{e!O1j3ZpI==@GA8XK zo^h)bCbg#Rvh6XtmK&1e?waCI8s?nEZJy0-9u?+1ihhR=>4`dxQ!OW|5#xfjk5>Js zK<}pN82r%2Ui!K;p3igAR8Hw@%Tox>Uja2}8aA>-& zMpzUvpnmk6G4VpcAEAh09Yt6e`~!u@4%Y?2FiVnePCN4~jbLmDWoHpq-o&)q`p)M| z+#E@z_t&N>gwm*?+*KhSy;0pA)ymH=I6AlQ5}AGU_@Vw(MF>SQWRbmbhQmGhZAmCD zA$;8fPc3kQ;vAx#J{^o1kQlKRi`gv?U^&6R41sr(OvNErlxzRY^z;h0XX2j|g_d?X z$P#uDP86uP3B5bQ2)5^_MjmgGjv!`UAWWzhrq5RZB%u0W0WgXL`*wR_P?#XdUIq&w zu54CE|EUV)R8w7@`k7^Rr(2VEt5L4BbenoJ?Gy8V6+!#h)7AAS224sk4P$ExU4HFS16Forp#bSt#dT|K4Srme@kKX(+2XBAU7z9AAE>s;Wq@+h(3L)I zw*n66LR#U6@y^Uy>N@=kgOPY+EBEi@&qQ>fHDvVKKgcren3k~%H zj5~0ejp5IN*MC;Sej;T7%SLW?Xm!%qrHwD1zLJfAIz_IANb6^c5hvSEILJgH3F(pX z>C=mt(_x0x5&OS!36^<|Jte#*GJ-EI(CWL6v~1YLI@)(q<*nr(v4G&=ky;$9WLjBI z=^7~5pnY4MSX@vdA(;QgZ(XTZ?pT$yMGo#Ef=|389$`ebwwq3Z5&Swnt{tGkDoDmw zKnl<$ITxQ)=pyub&S>(7dixSLd+*T4-8sQyIJeN3AMM|XpG3xjdd!se4#FU{zjAY) z!rAX9j~~+&%395i<&QBn+j9Z(kvPgn-hbT@7+6Ova~PdVjx3Z2`xbt-LMX^o)P*s& z#}>t&&jHKDr?@K!ugyIj5|SI_116@ zt-fhn)2C8pS7_;X;*h?83l*RmYItJcY}h?JTNILK%u%(MO+q40fB6$kpmjZ@uEPpu zh{(P15^#nb0{90k>0glmkK1S9rq4zaQ2tLVU>p?o74Ch4Y=4|7uO(x2K(oTsi~@`V z0s+(o)2d!~1z}6+r3rZ(PHnuF@@%X6WzK)YShn(Wn9IiKnUk!1h+K#>5A0Sj65a6I z@0e3<6A&2S&^ycamrp$W?(dSDEzyno$yOi5>zP zEIM~m%f3R?8yUK}I;?qCypIOPlf)Hzac%6_Y~`dHqruC%6eHY9j2VyV>MBEMK<+*? z2=FTDH9D}g-9MdtQk>4b9dUnJ!?wr?K+gc%&o<5bm8kL>k%@8J@iEuqSK&S$=={7f zyGDP-9};PT}GCrKkojR%Biic%FqaYm-@7hdDpc`;7}lVcR^FCuMa>#EAJ9?<^ab@n}f*} zdXfU5!uLT$m>vVViZ0bS^Ydpe#(k6TfI*k_VH9ANFx?RgdCCLl!0666lOAWe4Zvhc zK-!9KDS?`lg^i}=Jk_1hMvZJaQ-}E-u&&8LAaV)p1sSx1x*zI;DU8QL$aFp2hArA* z8+M}x37RG6aM$xu-T;U_NJ^n8XF8-C8j!)>~5IUDhGZLvl#dU)Ge@I zg+iFdtx{I1^JpKLlnbnwMzwx@AGbF<;Ldc1TZ|Bkz^(vlHQiFf`FzDLO`c5w#RA2f zm|K>(jdj*wGpJqqmzN**GFpTtJag@y^(I+Q&2B4?Kc=9-;4^(cBp_UAS$ z+FE_*ERW{pLq_Afah^T`9jGqI8DzliI!mC-v)n=HW=jh+*NNR^(O zN(;Zw%5+KAkZt_T!zQRgtA8u)cvwJ4Ad!#R%=7W`c2WQ0RloCSmr9ZEi6QhL*B}Mt z6l>7sS&Js>5@wiaf}A4x?~J}-{(AILVzR=vNRNBeG^Al?8K?OKATYKH>@0b*;S7BY z%shvV#582pH2I3t!&E5w1f;GN@P77amvodS&JTVmXkym} z^ho&^pQOqgG*i777iQ!~GxJ}ef?3Wf?x&+J@$=F?2nW*b*x-f`mJr^fp{}7@5c{(x zKj5Isn8Q3~NM+<@e6Do-czVTq(Y0-*K3%@CqUK(3m?WSx1wRLXkY3Va9ArRwnGN?9 zHaby2U?rp~wA-hg$#37;E{4_>d|7)Kk1&^*y0nU|wq>-bdxV=eWDJM0MK;P0&+PCY z-S$`SON?OOMIS!p=pz^euW#iePG#Bt>^mb$a0ucp9TrxzU4t7JyeDb^Tvi~5^>lUP zo}8zxJCJn2j<=!SL3RpxcSqs*6Bg%x`;*gJZd~=__i5Vg07nZ+P*>c>#_@Y)_F!f#4DZxk=*M}S~g-7eCD~;Q>Wm#{L9J?*tGaaHT z{h_%-tsPnqRm%pSUU4QNM3V!VCv&XHJn-EqVxfw(E%$WUqgK1B3G0ZqZZ7!X04fs> zBX=%(L+{ zQXkWqJJ35Je#!zfiHMgM9S@_ga^U!ty`u-k!%|G#8`|2B$GnWo{A}N+rs%Qum2g~B zWY_7|PM1cJ0>_->!)?O;=NXZ?v{sJ$WWCR=Nv^OhmN15(sLrYQyBD3jB|Khe|I&n4 zc6K7^+D5oITdf01MfhBa`W%{3z`Q8rGZyB>E3GcS>ldxks65$smO@|892g7FHc3$j zXh*&syusa$MPeVw?-DU&J9u1XP>w!IPwkfGEjrfBtKoPql-7A&Zk%sW0dNzP=TK^f zF>i?Ku5#hoNzJmGd}_jKwi7P-=Y5C!tooKytX9VHMRl~d=ix*}QBFn`?Afy>r8TO> zxrd@o!(Ckpa!`r3x1bAAK$+P0WC0vo~u}p1&o9U ze+=Bza&vPtF3Sekr++}d_t~-Id3s^1lEOfH?OuUsZyI`aVF^M1^BhjEGm^p@k)}ft zp6T^gLLoqq5E8aBxwBKh%4MU+uG)_6{x_GntFRV@$wc}dv?k6d>5oeTeNXtrQkZrC zcn6Y$+Xek0<)Wiioc&a8nB`$%EVN<+=geJx#Tr!imAUh}A>oe~Nnpf?$nA?(#w!(A z{e6ObEPRjnE~h3}AcX{315>kYmFO_HeP}V+e;v8KglQluJUmJ?V>sBR_u^JZfpi@4 zCE)Z(3i*g4Tr!WsyaMYy`9eE=js;x`F-2@k*>Xe70z@Gmg?Y7jd8&_AT|;Qzdd1j@ z3Gh4H`p(q76Oq2lz4zOclGs4akuI^SPyXdTZue7{{$+ij(6(e9lPb2(0YrBfYLqs|y&RGd-x;G(rtEj8p%>eI!QWsqvFlbd- zwD#4PH*a<|15zMi1K@0g}cgM7b7fWY&aZhoKbKy%F%!f$r{TA@;YIgioZ z4VKI5d`~SY4h@xfz!CREdUIY&sLSq-B@c(kpRt&mMQLs$5e>5*5=OZ`Fwg3Yr6v|{ z&`#NT==yv6LfmuzW*C5=0)h*mO#k|EJNBFxuDmn)c@95A8UsJJ0c^Yke0~;b+YqYK zf&zEmZeZGMDP9^SIVhDMiA;B-<2u z0%Z9X`iuWlb_0lE74*PWR`LxK#=Ol?d7WOZG9SXwZs^Z_jwwhfAV_hY1kwr>H&*Sn z{6S|Dl%P8hK#qhpAuAsfny(^De7(7CkG$s{8n7$?;mB;;;+Dc}7s3|P8Lo5IImsB(u{r8eJZA(ju4^ zB%?YtDB{T|T8W$k#0@FqA#cM*l6&~ZY~|y}-mr|ivC%F|Z}aP}06*W`Dxk5=^}1%4 zI%RdsdvW?Lc0tE5+PKB$^-8Orvh}U(YjLR%Hp!0Uyq7e6c3r~$&RxtU@}3EK$Ui~> z8I~KSd>Ks>7CCXr;l2SW6}9KwCP1l)rKKk%-@LIrv|;4u&4uF<0L1pc!9heObt(St zV?tqzT1F6I=mY*V=g_caS7E9qW@P>Sf-g2!a?K2Vv9dp!!>%bVwvoj=+>$|OxnMu7 z-oiJ*r8lu6qLX7j-cg%YuPAtMwJQ7U#;_P$@|A8}%MClw)7|&~H_LBxUJfAxjK~jPVYv$nghF@Lc zVPg25X<1HFyemm}VHbSNps;9Yyo#zgmlSqN&_B{wV)}NaJ@e0}a%xp`7A@i3ISR62 z;gKFW%Pt+Eept3o$lu6QXy`H2q!>!~7dIO0OF%Bjve8yra6$A5ZWz1L*;~w2EjpYS z`U%DcwJPa!AQ#RkA@|x;+|XxD{p`-uOZ=35T#Pfb$KkQG3 zTkJonf)yMh`!G(=p|gM@N%^;Cym^t!^4`qkm70(WBJdEq5sdW84X`<&|GAA2HGl>r zb|ya`Ufo+ImJ^^H5^T^T?OI2N2-4LL0Agpco}Hd+v9vL(hWrqfCq0a|diq4Z^=)Iz zM**S;-(ek1J*1`8=p_<^BX7_GwGp5|f<}<7F{imHm<1BD5b=zg%?FL<_#<1cmLW6}qnD?^a}&}Y%o$b`H1V9YKbk2)7&fs30|ql!2EZ|T_7!{T zt5DSUq2b}zfGuWiIb?NAeErT4*Nnqk&Fp5FaJK`XKFsP1iPNB|L8j4jc*k;F^3~b# zTj7&Q<%JT{o)fKETYEeBOu)XH!!InzqT0%a5W7tVniyY@&Uf!u)tzl-WzCvKn8ajk zPW^NN>TDHOLLYwg`g0asF}icKAG-Sbhot$FFyjg@8)yik_KLc)A{s?MBixM)>;}R4 zA*bQLGi?LV;&~B;57;_RRrR|*gxz^dtvC==6VkHgaYe;xlPDw&+M5+WE;B!Bh}lzs z#U|fd1g~Vf26sN8-(kEkTi`=whv$CAa#h5NSIeH+@(2VAnc3Y-YnVLm9UKOq8V~qn zJa6FB|Lkm24<|TAqu2Ypno2L7zoUJOF@?d3gSV%gSwgAOQ0 zZsyE0&%|P}@Xc?2^Qi2;{At3lutuhZRh}f8?Ze#O&43u-DZtgh9NqhVpaM+`sw8Z{ z1Yja?90T2G${7$8EYfZJ4Df{v->>Xv25<%gc?>9APEY=Irup2)q}x{t z2#3G`c9MKSb23 zZCzLpgEoi@jjJqlAqXK0sg)vb1jQmq$U+gZSxRxClDe>|_RlsZF|ENilR(uJ>t9+m zRx`0qdt971^=3MMNsKh|K5$9%)0sP$d!Bi}@7_7*a>_3vdKItO67D9MK z@?9Hd7j=#STeW}aHD04KbRB7ShV><6wkF274LCi^PX=61np6K|tV zpl-2Xs1xV@yc3A^-s%WFLD+d~L_B}&3Nm7Xglu7`Cm@R|l=HBLz`-M|t_>Shg^^(qm|F)8^R)YhZah9ubrJW6U_ zPxCElm7g1!xdKcuGImWnf_7qC4!} z!*as96j`bPe}9F)Iji*054=vAq;0vxvRMPV68%akyJL*qA%x;abQ`d(Lci-eZII?( z>QTzusd6ZrN;ZTLUe%zlg(;Pv!@vPj6aQT1*t`NfTjM$VGikBfQN89p`dQF%YcBdf zzp4*6ozCv7@*F25(38MdKwpe;AcQbEmp7^N=g*VR=P%5{VJxiwCXUW8%HLf=Rt4Y_ z4d7kCAphK(v_hhIu)<>=m7I2InWu^3clQuN1Mni{ukcNCT!VQxurJ2=eVNz0DTMH{ zl3v*)0(=VOB+!2K(6Rzu20Q~iP4zYPhLjTbE8UG{1u7c#Jf+U*6%u|igDQW&)7NAA z_xQhAx`hxZ6bcLaVRZ}3l#KWDU|$HKh159zQG@UZ7vE}t62Qk5B-I7HtDaD$9!eK8 zukK};P +/// Event's schema definition +/// Return type of each method should be +///

+[EventsContract(EventsContractType.Consumer)] +[EventsContract(EventsContractType.Producer)] +[Obsolete("Either use the Producer or Consumer version of this interface", true)] +public interface IProductCycle +{ + ValueTask IdeaAsync(string title, string describe); + ValueTask PlanedAsync(string id, Version version, string doc); + ValueTask ReviewedAsync(string id, Version version, params string[] notes); + ValueTask ImplementedAsync(string id, Version version); + ValueTask TestedAsync(string id, Version version, params string[] notes); + ValueTask DeployedAsync(string id, Version version); + ValueTask RejectedAsync(string id, Version version, string operation, NextStage nextStage, params string[] notes); +} \ No newline at end of file diff --git a/Tests/Specialized/Tests.Events.WebTest.Abstractions/NextStage.cs b/Tests/Specialized/Tests.Events.WebTest.Abstractions/NextStage.cs new file mode 100644 index 00000000..ee65c86c --- /dev/null +++ b/Tests/Specialized/Tests.Events.WebTest.Abstractions/NextStage.cs @@ -0,0 +1,7 @@ +namespace Tests.Events.WebTest.Abstractions; + +public enum NextStage +{ + Reject, + Abandon +} diff --git a/Tests/Specialized/Tests.Events.WebTest.Abstractions/ProductCycleConstants.cs b/Tests/Specialized/Tests.Events.WebTest.Abstractions/ProductCycleConstants.cs new file mode 100644 index 00000000..1b69cd17 --- /dev/null +++ b/Tests/Specialized/Tests.Events.WebTest.Abstractions/ProductCycleConstants.cs @@ -0,0 +1,10 @@ +namespace Tests.Events.WebTest.Abstractions; + +/// +/// Common constants +/// +public static class ProductCycleConstants +{ + public const string URI = "event-demo"; + public const string CONSUMER_GROUP = "main"; +} diff --git a/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj b/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj new file mode 100644 index 00000000..a4840b31 --- /dev/null +++ b/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj @@ -0,0 +1,18 @@ + + + + + + + + + + + True + + + + + + + diff --git a/Tests/Specialized/Tests.Events.WebTest.Abstractions/icon.png b/Tests/Specialized/Tests.Events.WebTest.Abstractions/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 Date: Mon, 12 Jun 2023 11:14:02 +0000 Subject: [PATCH 155/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 96111474..ecb8202a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.114 + 1.2.115 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 1d8ee11efc765ad3f95b70f0a54e8248e0513cf9 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 13 Jun 2023 15:57:23 +0300 Subject: [PATCH 156/178] feat: re-module telemetry registration (move features to the template) --- .../RedisConsumerChannel.cs | 24 +-- .../RedisCommonProviderExtensions.cs | 31 ++- ...one.Channels.S3StoreProvider.Common.csproj | 2 +- Directory.Build.props | 2 + ...g.Backbone.OpenTelemetry.Extensions.csproj | 1 + .../EventSourcingOtel.cs | 142 +++++-------- .../Types/MetricsExporters.cs | 14 ++ .../Types/TraceExporters.cs | 13 ++ .../Program.cs | 4 +- .../Extensions/ConsumerExtensions.cs | 32 +-- .../Program.cs | 6 +- .../ProductCycleProducerExtensions.cs | 34 +-- .../Program.cs | 14 +- .../Tests.Events.WebTest.Abstractions.csproj | 4 +- .../Extensions/ConsumerExtensions.cs | 12 +- .../Extensions/OpenTelemetryExtensions.cs | 195 ++++++------------ .../ShipmentTrackingProducerExtensions.cs | 16 +- Tests/WebSampleS3/Program.cs | 17 +- Tests/WebSampleS3/WebSampleS3.csproj | 3 +- 19 files changed, 262 insertions(+), 304 deletions(-) create mode 100644 Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/MetricsExporters.cs create mode 100644 Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index f8ec4de9..d72a9e0c 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -481,19 +481,19 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( catch (RedisServerException ex) { logger?.LogWarning(ex, ex.Message); - //await _connFactory.CreateConsumerGroupIfNotExistsAsync( - // key, - // plan.ConsumerGroup, - // logger); - } - catch (Exception ex) - { - logger?.LogWarning(ex, ex.Message); - //await _connFactory.CreateConsumerGroupIfNotExistsAsync( - // key, - // plan.ConsumerGroup, - // logger); + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger); } + //catch (Exception ex) + //{ + // logger?.LogWarning(ex, ex.Message); + // //await _connFactory.CreateConsumerGroupIfNotExistsAsync( + // // key, + // // plan.ConsumerGroup, + // // logger); + //} #endregion // Exception Handling } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index e13f742c..57b0ce09 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Logging; +using System.Net.Http.Headers; + +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -121,15 +123,36 @@ await db.StreamCreateConsumerGroupAsync(eventSourceKey, catch (RedisServerException ex) { - logger.LogWarning(ex.FormatLazy(), $"{nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: failed & still waiting {CurrentInfo()}"); + logger.LogWarning(ex.FormatLazy(), $""" + {nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: + failed & still waiting + {CurrentInfo()} + """); } catch (RedisConnectionException ex) { - logger.LogWarning(ex.FormatLazy(), $"{nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: connection failure {CurrentInfo()}"); + logger.LogWarning(ex.FormatLazy(), $""" + {nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: + Connection failure + {CurrentInfo()} + """); + } + catch (ObjectDisposedException ex) + { + logger.LogWarning( $""" + {nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: + Connection might not being available + {CurrentInfo()} + """); } + catch (Exception ex) { - logger.LogWarning(ex.FormatLazy(), $"{nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: unexpected failure {CurrentInfo()}"); + logger.LogWarning( ex, $""" + {nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: + unexpected failure + {CurrentInfo()} + """); } #endregion // Exception Handling diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index 81cc428c..f1270c9b 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/Directory.Build.props b/Directory.Build.props index ecb8202a..3854167c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,6 +6,8 @@ Breaking changes: S3Strategy was renamed to S3Storage # 1.2.96 Breaking changes: Method on the consumer interface generated with first parameter of type ConsumerMetadata + # 1.2.115 + Breaking changes: registration extensions was re-module diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index 0fabcd90..272311e1 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -22,6 +22,7 @@ + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index 188addd4..aa0909b8 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -1,9 +1,6 @@ -using System.Diagnostics; +using Microsoft.Extensions.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Hosting; - -using OpenTelemetry.Exporter; +using OpenTelemetry; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -14,8 +11,9 @@ namespace Microsoft.Extensions.DependencyInjection; + ///

-/// core extensions for ASP.NET Core +/// core extensions for ASP.NET Core /// public static class EventSourcingOtel { @@ -28,119 +26,73 @@ public static class EventSourcingOtel ///
public const string REDIS_PRODUCER_CHANNEL_SOURCE = "redis-producer-channel"; - /// - /// Adds the event consumer telemetry source (will result in tracing the consumer). - /// - /// The builder. - /// - private static TracerProviderBuilder ListenToEventSourceRedisChannel( - this TracerProviderBuilder builder) => - builder.AddSource( - REDIS_CONSUMER_CHANNEL_SOURCE, - REDIS_PRODUCER_CHANNEL_SOURCE); + #region WithEventSourcingTracing /// - /// Adds the open-telemetry binding. + /// Adds the open-telemetry tracing binding. /// - /// The services. + /// The build. /// The host env. - /// The filter. - /// The sampler. - /// The additional list of subscribe sources for the telemetry. + /// Enable to inject additional setting. /// - public static IServiceCollection AddOpenTelemetryForEventSourcing( - this IServiceCollection services, + public static OpenTelemetryBuilder WithEventSourcingTracing( + this OpenTelemetryBuilder builder, IHostEnvironment hostEnv, - Func? filter = null, - Sampler? sampler = null, - params string[] additionalSources) + Action? injection = null) { // see: // https://opentelemetry.io/docs/instrumentation/net/getting-started/ // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables - Func filtering = filter ?? (Func)OpenTelemetryFilter; - var appName = hostEnv.ApplicationName; -#pragma warning disable S125 // Sections of code should not be commented out - services.AddOpenTelemetry() + builder .WithTracing(tracerProviderBuilder => { var sources = new[] { appName, REDIS_CONSUMER_CHANNEL_SOURCE, REDIS_PRODUCER_CHANNEL_SOURCE}; - if(additionalSources != null && additionalSources.Length != 0) - { - sources = sources.Concat(additionalSources).ToArray(); - } tracerProviderBuilder .AddSource(sources) - .ConfigureResource(resource => resource.AddService(appName)) - .AddAspNetCoreInstrumentation(m => - { - m.Filter = filtering; - // m.Enrich - m.RecordException = true; - m.EnableGrpcAspNetCoreSupport = true; - }) - .AddHttpClientInstrumentation(m => - { - // m.Enrich - m.RecordException = true; - }); - if (sampler != null) - tracerProviderBuilder.SetSampler(sampler); - tracerProviderBuilder.AddOtlpExporter(); - if (hostEnv.IsDevelopment()) - { - tracerProviderBuilder.AddConsoleExporter(options => - options.Targets = ConsoleExporterOutputTargets.Console); - } - }) - .WithMetrics(metricsProviderBuilder => + .ConfigureResource(resource => resource.AddService(appName)); + + injection?.Invoke(tracerProviderBuilder); + }); + + return builder; + } + + #endregion // WithEventSourcingTracing + + #region WithEventSourcingMetrics + + /// + /// Adds the open-telemetry metrics binding. + /// + /// The build. + /// The host env. + /// The injection. + /// + public static OpenTelemetryBuilder WithEventSourcingMetrics( + this OpenTelemetryBuilder builder, + IHostEnvironment hostEnv, + Action? injection = null) + { + // see: + // https://opentelemetry.io/docs/instrumentation/net/getting-started/ + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + + var appName = hostEnv.ApplicationName; + builder.WithMetrics(metricsProviderBuilder => { metricsProviderBuilder - .ConfigureResource(resource => resource.AddService(appName)) - .AddMeter(appName) - .AddAspNetCoreInstrumentation( - //m => { - // m.Filter = (_, ctx) => filtering(ctx); - //} - ) - .AddOtlpExporter(); - //if (hostEnv.IsDevelopment()) - // metricsProviderBuilder.AddConsoleExporter(); + .ConfigureResource(resource => resource.AddService(appName)); + injection?.Invoke(metricsProviderBuilder); }); - return services; -#pragma warning restore S125 // Sections of code should not be commented out - - #region OpenTelemetryFilter - - bool OpenTelemetryFilter(HttpContext context) => OpenTelemetryFilterMap(context.Request.Path.Value); - - bool OpenTelemetryFilterMap(string? path) - { - if (string.IsNullOrEmpty(path) || - path == "/health" || - path == "/readiness" || - path == "/version" || - path == "/settings" || - path.StartsWith("/v1/kv/") || // configuration - path == "/api/v2/write" || // influx metrics - path == "/_bulk" || - path.StartsWith("/swagger") || - path.IndexOf("health-check") != -1 || - path == "/_framework/aspnetcore-browser-refresh.js" || - path.StartsWith("/_vs/")) - { - return false; - } - return true; - } - - #endregion // OpenTelemetryFilter + return builder; } + + #endregion // WithEventSourcingMetrics } diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/MetricsExporters.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/MetricsExporters.cs new file mode 100644 index 00000000..3dbaf238 --- /dev/null +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/MetricsExporters.cs @@ -0,0 +1,14 @@ +namespace EventSourcing.Backbone; + +/// +/// Metrics exporter options (open-telemetry) +/// +[Flags] +public enum MetricsExporters +{ + None = 0, + Otlp = 1, + DevConsole = Otlp * 2, + Prometheus = DevConsole * 2, + Default = Otlp | Prometheus +} \ No newline at end of file diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs new file mode 100644 index 00000000..ca464505 --- /dev/null +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs @@ -0,0 +1,13 @@ +namespace EventSourcing.Backbone; + +/// +/// Tracing exporter options (open-telemetry) +/// +[Flags] +public enum TraceExporters +{ + None = 0, + Otlp = 1, + DevConsole = Otlp * 2, + Default = Otlp | DevConsole +} \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index c1e7bf2b..88f17925 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -74,7 +74,9 @@ }); //IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); -services.AddOpenTelemetryForEventSourcing(environment); +services.AddOpenTelemetry() + .WithEventSourcingTracing(environment) + .WithEventSourcingMetrics(environment); //services.AddOpenTelemetry(environment, shortAppName, redisConnection); diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs index 8e4235e7..e981b490 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs @@ -12,37 +12,41 @@ public static class ConsumerExtensions /// /// Register a consumer. /// - /// The services. + /// The builder. /// The URI. - /// The environment. /// - public static IServiceCollection AddConsumer ( - this IServiceCollection services, - string uri, - Env env) + public static WebApplicationBuilder AddConsumer ( + this WebApplicationBuilder builder, + string uri) { + IServiceCollection services = builder.Services; + IWebHostEnvironment environment = builder.Environment; + string env = environment.EnvironmentName; + services.AddSingleton(ioc => { return BuildConsumer(uri, env, ioc ); }); - return services; + return builder; } /// /// Register a consumer when the URI of the service used as the registration's key. /// See: https://medium.com/weknow-network/keyed-dependency-injection-using-net-630bd73d3672 /// - /// The services. + /// The builder. /// The URI of the stream (which is also used as the DI key). - /// The environment. /// - public static IServiceCollection AddKeyedConsumer ( - this IServiceCollection services, - string uri, - Env env) + public static WebApplicationBuilder AddKeyedConsumer ( + this WebApplicationBuilder builder, + string uri) { + IServiceCollection services = builder.Services; + IWebHostEnvironment environment = builder.Environment; + string env = environment.EnvironmentName; + services.AddKeyedSingleton(ioc => { return BuildConsumer(uri @@ -51,7 +55,7 @@ public static IServiceCollection AddKeyedConsumer ( ); }, uri); - return services; + return builder; } /// diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs index 2f9da5d8..f1f485ca 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs @@ -13,10 +13,12 @@ IWebHostEnvironment environment = builder.Environment; string env = environment.EnvironmentName; -services.AddOpenTelemetryForEventSourcing(environment); +services.AddOpenTelemetry() + .WithEventSourcingTracing(environment) + .WithEventSourcingMetrics(environment); services.AddEventSourceRedisConnection(); -services.AddKeyedConsumer(ProductCycleConstants.URI, env); +builder.AddKeyedConsumer(ProductCycleConstants.URI); services.AddHostedService(); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs index 6df08a56..7ad5a6af 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs @@ -14,46 +14,48 @@ public static class ProductCycleProducerExtensions /// /// Register a producer. /// - /// The services. + /// The builder. /// The URI. - /// The environment. /// - public static IServiceCollection AddProductCycleProducer - ( - this IServiceCollection services, - string uri, - Env env) + public static WebApplicationBuilder AddProductCycleProducer( + this WebApplicationBuilder builder, + string uri) { + IServiceCollection services = builder.Services; + IWebHostEnvironment environment = builder.Environment; + string env = environment.EnvironmentName; + services.AddSingleton(ioc => { return BuildProducer(uri, env, ioc ); }); - return services; + return builder; } /// /// Register a producer when the URI of the service used as the registration's key. /// See: https://medium.com/weknow-network/keyed-dependency-injection-using-net-630bd73d3672. /// - /// The services. + /// The builder. /// The URI. - /// The environment. /// - public static IServiceCollection AddKeyedProductCycleProducer - ( - this IServiceCollection services, - string uri, - Env env) + public static WebApplicationBuilder AddKeyedProductCycleProducer( + this WebApplicationBuilder builder, + string uri) { + IServiceCollection services = builder.Services; + IWebHostEnvironment environment = builder.Environment; + string env = environment.EnvironmentName; + services.AddKeyedSingleton(ioc => { return BuildProducer(uri, env, ioc ); }, uri); - return services; + return builder; } private static IProductCycleProducer BuildProducer(string uri, Env env, IServiceProvider ioc diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs index 310bbf96..37a9f1b8 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs @@ -1,7 +1,7 @@ +using Microsoft.OpenApi.Models; + using Tests.Events.ProducerWebTest; using Tests.Events.WebTest.Abstractions; -using Tests.Events.ProducerWebTest.Controllers; -using Microsoft.OpenApi.Models; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -13,10 +13,12 @@ IWebHostEnvironment environment = builder.Environment; string env = environment.EnvironmentName; -services.AddOpenTelemetryForEventSourcing(environment); +services.AddOpenTelemetry() + .WithEventSourcingTracing(environment) + .WithEventSourcingMetrics(environment); services.AddEventSourceRedisConnection(); -services.AddKeyedProductCycleProducer(ProductCycleConstants.URI, env); +builder.AddKeyedProductCycleProducer(ProductCycleConstants.URI); @@ -42,7 +44,7 @@

docker run --name jaeger-otel --rm -it -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest

", }); - }); + }); var app = builder.Build(); @@ -63,5 +65,5 @@ List switches = new(); switches.Add("Producer"); logger.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); - + app.Run(); diff --git a/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj b/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj index a4840b31..e7df0a73 100644 --- a/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj +++ b/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj @@ -1,8 +1,8 @@ - - + + diff --git a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs index 2d1fba6e..292568a4 100644 --- a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs @@ -13,18 +13,20 @@ public static class ConsumerExtensions /// /// Adds the shipment tracking producer. /// - /// The services. + /// The builder. /// The URI. /// The s3 bucket. - /// The environment. /// public static IServiceCollection AddShipmentTrackingConsumer ( - this IServiceCollection services, + this WebApplicationBuilder builder, string uri, - string s3Bucket, - Env env) + string s3Bucket) { + IServiceCollection services = builder.Services; + IWebHostEnvironment environment = builder.Environment; + string env = environment.EnvironmentName; + var s3Options = new S3Options { Bucket = s3Bucket }; services.AddSingleton(ioc => { diff --git a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs index 6d3d008b..265d3680 100644 --- a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs +++ b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs @@ -1,149 +1,82 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -using EventSourcing.Backbone; - -using OpenTelemetry.Exporter; -using OpenTelemetry.Resources; +using OpenTelemetry.Metrics; using OpenTelemetry.Trace; -using WebSample.Extensions; - - -// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 +// see: +// https://opentelemetry.io/docs/instrumentation/net/getting-started/ +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables +namespace WebSampleS3; -namespace WebSample.Extensions +/// +/// Open telemetry extensions for ASP.NET Core +/// +internal static class OpenTelemetryExtensions { + #region AddOpenTelemetryEventSourcing + /// - /// open telemetry extensions for ASP.NET Core + /// Adds open telemetry for event sourcing. /// - public static class OpenTelemetryExtensions + /// The builder. + /// + public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicationBuilder builder) { - #region AddOpenTelemetry - - /// - /// Adds the open-telemetry binding. - /// - /// The services. - /// The host env. - /// Short name of the application. - /// An open telemetry sampler. - /// The telemetry path filter. - /// - public static IServiceCollection AddOpenTelemetry( - this IServiceCollection services, - IHostEnvironment hostEnv, - string appName, - Sampler? sampler = null, - Func? telemetryPathFilter = null) - { - // see: https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables - - Console.WriteLine($"JAEGER endpoint: key='OTEL_EXPORTER_JAEGER_ENDPOINT', env='{hostEnv.EnvironmentName}'"); // will be visible in the pods logs - -#pragma warning disable S125 // Sections of code should not be commented out - services.AddOpenTelemetry() - .WithTracing(builder => - { - TracerProviderBuilder tracerProviderBuilder = - builder.SetResourceBuilder(ResourceBuilder.CreateDefault() - .AddService(appName)) - .ListenToEventSourceRedisChannel() - .AddAspNetCoreInstrumentation(m => + IWebHostEnvironment environment = builder.Environment; + IServiceCollection services = builder.Services; + + // see: + // https://opentelemetry.io/docs/instrumentation/net/getting-started/ + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + services.AddOpenTelemetry() + .WithEventSourcingTracing(environment, + cfg => + { + cfg.AddAspNetCoreInstrumentation(m => { - m.Filter = OpenTelemetryFilter; - // m.Enrich + m.Filter = TraceFilter; m.RecordException = true; m.EnableGrpcAspNetCoreSupport = true; }) - .AddHttpClientInstrumentation(m => - { - // m.Enrich - m.RecordException = true; - }) - //.AddRedisInstrumentation(redisConnection - // //, m => { - // // m.FlushInterval - // //} - // ) - .AddOtlpExporter(); - if (sampler == null) - tracerProviderBuilder.SetSampler(); - else - tracerProviderBuilder.SetSampler(sampler); - - if (hostEnv.IsDevelopment()) - { - builder.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Console); - } - }); - return services; -#pragma warning restore S125 // Sections of code should not be commented out - - #region OpenTelemetryFilter - - bool OpenTelemetryFilter(HttpContext context) - { - string? path = context.Request.Path.Value; - return telemetryPathFilter?.Invoke(path) ?? OpenTelemetryFilterMap(path); - } - - bool OpenTelemetryFilterMap(string? path) - { - if (string.IsNullOrEmpty(path) || - path == "/health" || - path == "/readiness" || - path == "/version" || - path == "/settings" || - path == "/api/v2/write" || // influx metrics - path == "/_bulk" || - path.StartsWith("/swagger") || - path.IndexOf("health-check") != -1) + .AddHttpClientInstrumentation(m => + { + // m.Enrich + m.RecordException = true; + }) + .AddOtlpExporter(); + if (environment.IsDevelopment()) + cfg.AddConsoleExporter(); + }) + .WithEventSourcingMetrics(environment, cfg => { - return false; - } - return true; - } - - #endregion // OpenTelemetryFilter - } - - #endregion // AddOpenTelemetry - - #region WithJsonOptions - - /// - /// Set Controller's with the standard json configuration. - /// - /// The controllers. - /// - public static IMvcBuilder WithJsonOptions( - this IMvcBuilder controllers) - { - return controllers.AddJsonOptions(options => - { - // https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to - JsonSerializerOptions setting = options.JsonSerializerOptions; - setting.WithDefault(); - - }); - } + cfg.AddAspNetCoreInstrumentation( /* m => m.Filter = filter */) + .AddOtlpExporter() + .AddPrometheusExporter(); + if (environment.IsDevelopment()) + cfg.AddConsoleExporter(); + }); - #endregion // WithJsonOptions + return services; + } - #region WithDefault + #endregion // AddOpenTelemetryEventSourcing - public static JsonSerializerOptions WithDefault(this JsonSerializerOptions options) - { - options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - options.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; - options.WriteIndented = true; - options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); - options.Converters.Add(JsonMemoryBytesConverterFactory.Default); - return options; - } + #region TraceFilter - #endregion // WithDefault - } + /// + /// Telemetries the filter. + /// + /// The CTX. + /// + private static bool TraceFilter(HttpContext ctx) => ctx.Request.Path.Value switch + { + "/health" => false, + "/readiness" => false, + "/metrics" => false, + string x when x.StartsWith("/swagger") => false, + string x when x.StartsWith("/_framework/") => false, + string x when x.StartsWith("/_vs/") => false, + _ => true + }; + + #endregion // TraceFilter } diff --git a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs index a15b8bac..0cd82be0 100644 --- a/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ShipmentTracking/ShipmentTrackingProducerExtensions.cs @@ -13,18 +13,20 @@ public static class ShipmentTrackingProducerExtensions /// /// Adds the shipment tracking producer. /// - /// The services. + /// The builder. /// The URI. /// The s3 bucket. - /// The environment. /// - public static IServiceCollection AddShipmentTrackingProducer + public static WebApplicationBuilder AddShipmentTrackingProducer ( - this IServiceCollection services, + this WebApplicationBuilder builder, string uri, - string s3Bucket, - Env env) + string s3Bucket) { + IWebHostEnvironment environment = builder.Environment; + string env = environment.EnvironmentName; + IServiceCollection services = builder.Services; + var s3Options = new S3Options { Bucket = s3Bucket }; services.AddSingleton(ioc => { @@ -38,6 +40,6 @@ public static IServiceCollection AddShipmentTrackingProducer return producer; }); - return services; + return builder; } } diff --git a/Tests/WebSampleS3/Program.cs b/Tests/WebSampleS3/Program.cs index 2ec35e56..177977da 100644 --- a/Tests/WebSampleS3/Program.cs +++ b/Tests/WebSampleS3/Program.cs @@ -1,9 +1,13 @@ using Amazon.S3; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + using WebSample.Extensions; using WebSampleS3; + var builder = WebApplication.CreateBuilder(args); builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); @@ -12,18 +16,15 @@ // Add services to the container. -IWebHostEnvironment environment = builder.Environment; -string env = environment.EnvironmentName; -string appName = environment.ApplicationName; - -builder.Services.AddOpenTelemetry(environment, appName); +builder.AddOpenTelemetryEventSourcing(); string URI = "shipment-tracking"; // make sure to create the bucket on AWS S3 with both prefix 'dev.' and 'prod.' and any other environment you're using (like staging,etc.) string s3Bucket = "shipment-tracking-sample"; -builder.Services.AddShipmentTrackingProducer(URI, s3Bucket, env); -builder.Services.AddShipmentTrackingConsumer(URI, s3Bucket, env); + +builder.AddShipmentTrackingProducer(URI, s3Bucket); +builder.AddShipmentTrackingConsumer(URI, s3Bucket); builder.Services.AddHostedService(); @@ -34,6 +35,8 @@ var app = builder.Build(); +app.UseOpenTelemetryPrometheusScrapingEndpoint(); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index 650e92c7..4da314c1 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -19,7 +19,7 @@ - + @@ -40,6 +40,7 @@ + From 292254abcdbe4dff26b8046b462ebdf136a5f88a Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 13 Jun 2023 12:57:51 +0000 Subject: [PATCH 157/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3854167c..f2ce40a7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.115 + 1.2.116 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From ae347c93b6eeabd2cb64695b23019dc399c8241c Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:37:00 +0300 Subject: [PATCH 158/178] ci: fix build --- .../RedisConsumerChannel.cs | 4 ++-- .../RedisCommonProviderExtensions.cs | 10 ++++------ .../Builder/ConsumerBase.EventSourceSubscriber.cs | 2 +- .../Consumer/ConsumerMetadata.cs | 2 +- .../Consumer/Provider/IConsumerChannelProvider.cs | 2 +- EventSourcing.Backbone.Abstractions/Env.cs | 3 +-- EventSourcing.Backbone.sln | 4 ---- .../Types/TraceExporters.cs | 2 +- .../Channels/ConsumerTestChannel.cs | 2 +- .../Contracts/ISimpleEvent.cs | 4 +--- .../EventSourcing.Backbone.UnitTests/EndToEndTests.cs | 2 +- .../AspCoreExtensions.cs | 2 +- .../Controllers/ProducerController.cs | 4 ---- .../Jobs/ConsumerJob.cs | 6 +++--- .../ProductCycle/Entities/Idea.cs | 2 +- .../ProductCycle/Extensions/ConsumerExtensions.cs | 7 ++----- .../ProductCycle/ProductCycleProducerExtensions.cs | 5 +---- .../ProductCycle/IProductCycle.cs | 1 - Tests/EventSourcing.Backbone.WebEventTest/Program.cs | 2 -- Tests/HelloWorld/ConsumingEvents/Program.cs | 2 +- .../Controllers/ProductCycleConsumerController.cs | 7 +++++-- .../Extensions/ConsumerExtensions.cs | 6 +++--- .../Jobs/ProductCycleConsumerJob.cs | 8 ++++---- .../Tests.Events.ConsumerWebTest.Service/Program.cs | 9 +++++---- .../Tests.Events.ConsumerWebTest.Service.csproj | 6 +++++- .../Controllers/ProductCycleProducerController.cs | 8 +++++--- .../Entities/Idea.cs | 2 +- .../Entities/Review.cs | 4 +--- .../Entities/Test.cs | 4 +--- .../ProductCycle/ProductCycleProducerExtensions.cs | 1 + .../Tests.Events.ProducerWebTest.Service.csproj | 6 +++++- .../Tests.Events.WebTest.Abstractions.csproj | 4 ++++ .../WebSampleS3/Extensions/OpenTelemetryExtensions.cs | 6 +++--- Tests/WebSampleS3/Program.cs | 3 --- .../Concrete/ContractIncrementalGenerator.cs | 4 ++-- 35 files changed, 68 insertions(+), 78 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index d72a9e0c..6e7c09e8 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -139,7 +139,7 @@ public RedisConsumerChannel( /// /// When completed /// - public async ValueTask SubsribeAsync( + public async ValueTask SubscribeAsync( IConsumerPlan plan, Func> func, CancellationToken cancellationToken) @@ -478,7 +478,7 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( plan.ConsumerGroup, logger); } - catch (RedisServerException ex) + catch (RedisServerException ex) { logger?.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 57b0ce09..df9e821b 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -1,6 +1,4 @@ -using System.Net.Http.Headers; - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -137,9 +135,9 @@ Connection failure {CurrentInfo()} """); } - catch (ObjectDisposedException ex) + catch (ObjectDisposedException) { - logger.LogWarning( $""" + logger.LogWarning($""" {nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: Connection might not being available {CurrentInfo()} @@ -148,7 +146,7 @@ Connection might not being available catch (Exception ex) { - logger.LogWarning( ex, $""" + logger.LogWarning(ex, $""" {nameof(CreateConsumerGroupIfNotExistsAsync)}.StreamCreateConsumerGroupAsync: unexpected failure {CurrentInfo()} diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index b8ac23c0..5759753e 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -55,7 +55,7 @@ public EventSourceSubscriber( _maxMessages = _options.MaxMessages; _handlers = new ConcurrentQueue(handlers); - _subscriptionLifetime = channel.SubsribeAsync( + _subscriptionLifetime = channel.SubscribeAsync( plan, ConsumingAsync, _disposeCancellation.Token); diff --git a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs index c19c346c..dc470084 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs @@ -8,7 +8,7 @@ namespace EventSourcing.Backbone /// It represent the operation's intent or represent event. ///
[DebuggerDisplay("{Metadata.Uri} [{Metadata.MessageId} at {Metadata.ProducedAt}]")] - public sealed class ConsumerMetadata: IAckOperations + public sealed class ConsumerMetadata : IAckOperations { internal static readonly AsyncLocal _metaContext = new AsyncLocal(); diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerChannelProvider.cs b/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerChannelProvider.cs index 768781e1..d36017f4 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerChannelProvider.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerChannelProvider.cs @@ -15,7 +15,7 @@ public interface IConsumerChannelProvider /// /// When completed /// - ValueTask SubsribeAsync( + ValueTask SubscribeAsync( IConsumerPlan plan, Func> func, CancellationToken cancellationToken); diff --git a/EventSourcing.Backbone.Abstractions/Env.cs b/EventSourcing.Backbone.Abstractions/Env.cs index cd38edd4..8e58339b 100644 --- a/EventSourcing.Backbone.Abstractions/Env.cs +++ b/EventSourcing.Backbone.Abstractions/Env.cs @@ -1,6 +1,5 @@ -using System.Globalization; +using System.Text.Json; using System.Text.Json.Serialization; -using System.Text.Json; using static System.StringComparison; diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 1798ad5a..4559a248 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -192,25 +192,21 @@ Global {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Debug|Any CPU.Build.0 = Debug|Any CPU {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Gen|Any CPU.Build.0 = Gen|Any CPU {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {74F90FB5-198F-4B90-ACBD-F89E4F81FA1B}.Release|Any CPU.Build.0 = Release|Any CPU {8E90962C-870D-49C6-A260-585D16F13614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8E90962C-870D-49C6-A260-585D16F13614}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E90962C-870D-49C6-A260-585D16F13614}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {8E90962C-870D-49C6-A260-585D16F13614}.Gen|Any CPU.Build.0 = Gen|Any CPU {8E90962C-870D-49C6-A260-585D16F13614}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E90962C-870D-49C6-A260-585D16F13614}.Release|Any CPU.Build.0 = Release|Any CPU {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Gen|Any CPU.Build.0 = Gen|Any CPU {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED906FBD-F8BB-40D5-8629-00CE0F963681}.Release|Any CPU.Build.0 = Release|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Gen|Any CPU.ActiveCfg = Gen|Any CPU - {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Gen|Any CPU.Build.0 = Gen|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Release|Any CPU.ActiveCfg = Release|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs index ca464505..7beafe98 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/Types/TraceExporters.cs @@ -7,7 +7,7 @@ public enum TraceExporters { None = 0, - Otlp = 1, + Otlp = 1, DevConsole = Otlp * 2, Default = Otlp | DevConsole } \ No newline at end of file diff --git a/Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs b/Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs index 7a7db8e4..24b27af2 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Channels/ConsumerTestChannel.cs @@ -31,7 +31,7 @@ public ConsumerTestChannel(Channel channel) /// /// When completed /// - public async ValueTask SubsribeAsync( + public async ValueTask SubscribeAsync( IConsumerPlan plan, Func> func, CancellationToken cancellationToken) diff --git a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs index 0fddd083..37bd3271 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/Contracts/ISimpleEvent.cs @@ -1,6 +1,4 @@ -using System.ComponentModel; - -namespace EventSourcing.Backbone.UnitTests.Entities +namespace EventSourcing.Backbone.UnitTests.Entities { /// diff --git a/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs index 3d22ebac..2d3a37b2 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.UnitTests/EndToEndTests.cs @@ -125,7 +125,7 @@ public async Task End2End_CustomSubscriptionBridge_Test() await subscription.DisposeAsync(); await ch.Reader.Completion; - A.CallTo(() => _simpleEventConsumer.ExecuteAsync(A.Ignored,"Id", 1)) + A.CallTo(() => _simpleEventConsumer.ExecuteAsync(A.Ignored, "Id", 1)) .MustHaveHappenedOnceExactly(); A.CallTo(() => _simpleEventConsumer.RunAsync(A.Ignored, 1, A.Ignored)) .MustHaveHappenedOnceExactly(); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index 4390f01c..97f9bd3a 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -69,7 +69,7 @@ public static IServiceCollection AddOpenTelemetry( services.AddOpenTelemetry() .WithTracing(builder => { - var tracerProviderBuilder = builder.SetResourceBuilder(ResourceBuilder.CreateDefault() + builder.SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService(shortAppName)) .ListenToEventSourceRedisChannel() // .SetSampler() diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs index 7e07d724..46ad1d5a 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/ProducerController.cs @@ -1,9 +1,5 @@ -using EventSourcing.Backbone; - using Microsoft.AspNetCore.Mvc; -using EventSourcing.Backbone.WebEventTest; - namespace EventSourcing.Backbone.WebEventTest.Controllers; [ApiController] diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs index c4c2d6dc..93226d36 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/ConsumerJob.cs @@ -1,5 +1,4 @@ -using EventSourcing.Backbone; -using EventSourcing.Backbone.Building; +using EventSourcing.Backbone.Building; namespace EventSourcing.Backbone.WebEventTest; @@ -24,8 +23,9 @@ public sealed class ConsumerJob : IHostedService, IProductCycleConsumer /// Initializes a new instance. /// /// The logger. - /// The builder. + /// The consumer builder keyed. /// The producer. + /// consumer is missing DI public ConsumerJob( ILogger logger, IKeyed consumerBuilderKeyed, diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs index 4d3c6cc9..106836a0 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Entities/Idea.cs @@ -1,3 +1,3 @@ namespace EventSourcing.Backbone.WebEventTest; -public record Idea (string title, string describe); +public record Idea(string title, string describe); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs index 3be72e10..4abfacbf 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs @@ -1,7 +1,4 @@ -using EventSourcing.Backbone; - -// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 - +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 namespace EventSourcing.Backbone.WebEventTest; @@ -26,7 +23,7 @@ public static IServiceCollection AddConsumer Env env) { var s3Options = new S3Options { Bucket = s3Bucket }; - services.AddKeyedSingleton( ioc => + services.AddKeyedSingleton(ioc => { IConsumerReadyBuilder consumer = ioc.ResolveRedisConsumerChannel() diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs index e916bee3..89238fbd 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ProductCycle/ProductCycleProducerExtensions.cs @@ -1,7 +1,4 @@ -using EventSourcing.Backbone; - -// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 - +// Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 namespace EventSourcing.Backbone.WebEventTest; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs index d506413a..fe68ae35 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/IProductCycle.cs @@ -1,6 +1,5 @@ #pragma warning disable S1133 // Deprecated code should be removed -using EventSourcing.Backbone; namespace EventSourcing.Backbone.WebEventTest; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 88f17925..eb2fa717 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -9,8 +9,6 @@ using Refit; -using StackExchange.Redis; - var builder = WebApplication.CreateBuilder(args); builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); diff --git a/Tests/HelloWorld/ConsumingEvents/Program.cs b/Tests/HelloWorld/ConsumingEvents/Program.cs index b3f823d2..b7495500 100644 --- a/Tests/HelloWorld/ConsumingEvents/Program.cs +++ b/Tests/HelloWorld/ConsumingEvents/Program.cs @@ -13,7 +13,7 @@ class Subscription : IHelloEventsConsumer { public static readonly Subscription Instance = new Subscription(); - public ValueTask NameAsync(ConsumerMetadata meta,string name) + public ValueTask NameAsync(ConsumerMetadata meta, string name) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(); diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs index 161c43ad..e5bdac33 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Controllers/ProductCycleConsumerController.cs @@ -1,8 +1,11 @@ -using Tests.Events.WebTest.Abstractions; using System.Text.Json; + using EventSourcing.Backbone; + using Microsoft.AspNetCore.Mvc; +using Tests.Events.WebTest.Abstractions; + namespace Tests.Events.ConsumerWebTest.Controllers; [ApiController] @@ -17,7 +20,7 @@ public ProductCycleConsumerController( IKeyed consumerBuilderKeyed) { _logger = logger; - if (!consumerBuilderKeyed.TryGet(ProductCycleConstants.URI, out var consumerBuilder)) + if (!consumerBuilderKeyed.TryGet(ProductCycleConstants.URI, out var consumerBuilder)) throw new EventSourcingException($"The Consumer's registration found under the [{ProductCycleConstants.URI}] key."); _receiver = consumerBuilder.BuildReceiver(); } diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs index e981b490..891cdae1 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs @@ -15,7 +15,7 @@ public static class ConsumerExtensions /// The builder. /// The URI. /// - public static WebApplicationBuilder AddConsumer ( + public static WebApplicationBuilder AddConsumer( this WebApplicationBuilder builder, string uri) { @@ -25,7 +25,7 @@ public static WebApplicationBuilder AddConsumer ( services.AddSingleton(ioc => { - return BuildConsumer(uri, env, ioc + return BuildConsumer(uri, env, ioc ); }); @@ -39,7 +39,7 @@ public static WebApplicationBuilder AddConsumer ( /// The builder. /// The URI of the stream (which is also used as the DI key). /// - public static WebApplicationBuilder AddKeyedConsumer ( + public static WebApplicationBuilder AddKeyedConsumer( this WebApplicationBuilder builder, string uri) { diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs index 4cca3737..ece3a433 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Jobs/ProductCycleConsumerJob.cs @@ -1,8 +1,8 @@ -using Tests.Events.WebTest.Abstractions; - -using EventSourcing.Backbone; +using EventSourcing.Backbone; using EventSourcing.Backbone.Building; +using Tests.Events.WebTest.Abstractions; + namespace Tests.Events.ConsumerWebTest.Controllers; /// @@ -30,7 +30,7 @@ public ConsumerJob( ILogger logger, IKeyed consumerBuilderKeyed) { - if (!consumerBuilderKeyed.TryGet(ProductCycleConstants.URI, out var consumerBuilder)) + if (!consumerBuilderKeyed.TryGet(ProductCycleConstants.URI, out var consumerBuilder)) throw new EventSourcingException($"Consumer's registration found under the [{ProductCycleConstants.URI}] key."); _builder = consumerBuilder.WithLogger(logger); _logger = logger; diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs index f1f485ca..cd49b5c6 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs @@ -1,7 +1,8 @@ +using Microsoft.OpenApi.Models; + using Tests.Events.ConsumerWebTest; -using Tests.Events.WebTest.Abstractions; using Tests.Events.ConsumerWebTest.Controllers; -using Microsoft.OpenApi.Models; +using Tests.Events.WebTest.Abstractions; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -45,7 +46,7 @@

docker run --name jaeger-otel --rm -it -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:latest

", }); - }); + }); var app = builder.Build(); @@ -66,5 +67,5 @@ List switches = new(); switches.Add("Consumer"); logger.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); - + app.Run(); diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj index 7385f3c0..ac7e95a4 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj @@ -1,6 +1,10 @@ + + Debug;Release;Gen + false + - + 8e0c42af-b506-4d2b-bc6e-0566a6701529 Linux diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Controllers/ProductCycleProducerController.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Controllers/ProductCycleProducerController.cs index 3ba64bcd..f489a817 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Controllers/ProductCycleProducerController.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Controllers/ProductCycleProducerController.cs @@ -1,8 +1,10 @@ -using Tests.Events.WebTest.Abstractions; -using Tests.Events.ProducerWebTest.Service.Entities; using EventSourcing.Backbone; + using Microsoft.AspNetCore.Mvc; +using Tests.Events.ProducerWebTest.Service.Entities; +using Tests.Events.WebTest.Abstractions; + namespace Tests.Events.ProducerWebTest.Controllers; [ApiController] @@ -17,7 +19,7 @@ public ProductCycleProducerController( IKeyed producerKeyed) { _logger = logger; - if (!producerKeyed.TryGet(ProductCycleConstants.URI, out var producer)) + if (!producerKeyed.TryGet(ProductCycleConstants.URI, out var producer)) throw new EventSourcingException($"Producer's registration found under the [{ProductCycleConstants.URI}] key."); _producer = producer; } diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs index 9ed1c253..b8976d75 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Idea.cs @@ -1,3 +1,3 @@ namespace Tests.Events.ProducerWebTest.Service.Entities; -public record Idea (string title, string describe); +public record Idea(string title, string describe); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs index 4a574730..0874c21e 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Review.cs @@ -1,5 +1,3 @@ -using Tests.Events.WebTest.Abstractions; - -namespace Tests.Events.ProducerWebTest.Service.Entities; +namespace Tests.Events.ProducerWebTest.Service.Entities; public record Review(Id id, params string[] notes); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs index 4a2bc442..bcecae2b 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Entities/Test.cs @@ -1,5 +1,3 @@ -using Tests.Events.WebTest.Abstractions; - -namespace Tests.Events.ProducerWebTest.Service.Entities; +namespace Tests.Events.ProducerWebTest.Service.Entities; public record Test(Id id, params string[] notes); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs index 7ad5a6af..bdb7bdef 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Extensions/ProductCycle/ProductCycleProducerExtensions.cs @@ -1,4 +1,5 @@ using EventSourcing.Backbone; + using Tests.Events.WebTest.Abstractions; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj index 830a0057..4044a827 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj @@ -1,6 +1,10 @@ + + Debug;Release;Gen + false + - + 8c43cc25-10b6-412b-9772-94e1a069889a Linux diff --git a/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj b/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj index e7df0a73..554e3e61 100644 --- a/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj +++ b/Tests/Specialized/Tests.Events.WebTest.Abstractions/Tests.Events.WebTest.Abstractions.csproj @@ -1,4 +1,8 @@ + + Debug;Release;Gen + false + diff --git a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs index 265d3680..4451b47c 100644 --- a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs +++ b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs @@ -72,9 +72,9 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati "/health" => false, "/readiness" => false, "/metrics" => false, - string x when x.StartsWith("/swagger") => false, - string x when x.StartsWith("/_framework/") => false, - string x when x.StartsWith("/_vs/") => false, + string x when x.StartsWith("/swagger") => false, + string x when x.StartsWith("/_framework/") => false, + string x when x.StartsWith("/_vs/") => false, _ => true }; diff --git a/Tests/WebSampleS3/Program.cs b/Tests/WebSampleS3/Program.cs index 177977da..c0f0f7e4 100644 --- a/Tests/WebSampleS3/Program.cs +++ b/Tests/WebSampleS3/Program.cs @@ -1,8 +1,5 @@ using Amazon.S3; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - using WebSample.Extensions; using WebSampleS3; diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs index c1fb64eb..cc87b20d 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/ContractIncrementalGenerator.cs @@ -64,11 +64,11 @@ protected override GenInstruction[] OnGenerate( { string summaryEnds = "///
"; int idxRet = sb.ToString().IndexOf(summaryEnds); - if(idxRet != -1) + if (idxRet != -1) sb.Insert(idxRet + summaryEnds.Length, "\r\n\t\t/// The consumer metadata."); } builder.Append(sb); - + builder.Append("\t\tValueTask"); if (isProducer) builder.Append(""); From 7f95f7ac192a4f1baf99c1668eb9306c4b382658 Mon Sep 17 00:00:00 2001 From: bnayae Date: Tue, 13 Jun 2023 13:37:26 +0000 Subject: [PATCH 159/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index f2ce40a7..a7c57887 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.116 + 1.2.117 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From f896b6a3e161d194eca8c72321f4a58f663ef0f1 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:29:19 +0300 Subject: [PATCH 160/178] rfc: remove unused dependencies --- .../EventSourcing.Backbone.OpenTelemetry.Extensions.csproj | 5 ----- Tests/WebSampleS3/WebSampleS3.csproj | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index 272311e1..fb339b5e 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -20,12 +20,7 @@ - - - - - diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index 4da314c1..e81e8537 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -33,8 +33,10 @@ - + + + From db5dcd84867c7b17dea23d815bb9a45305d5596b Mon Sep 17 00:00:00 2001 From: bnayae Date: Wed, 14 Jun 2023 08:29:52 +0000 Subject: [PATCH 161/178] Increment Version --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index a7c57887..9e553924 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.2.117 + 1.2.118 # 1.2.85: Breaking changes: S3Strategy was renamed to S3Storage From 00f2b427f9d962c2310f0fbf5db6a9b65e5d891f Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 18 Jun 2023 09:33:06 +0300 Subject: [PATCH 162/178] rfc: clean up --- ...ing.Backbone.Channels.RedisConsumerProvider.csproj | 2 +- .../RedisConsumerChannel.cs | 11 ++--------- ...cing.Backbone.Channels.RedisProvider.Common.csproj | 2 +- ...ng.Backbone.Channels.S3StoreProvider.Common.csproj | 2 +- .../EventSourcing.Backbone.Consumers.csproj | 2 +- .../EventSourcing.Backbone.Abstractions.csproj | 5 +++-- ...tSourcing.Backbone.OpenTelemetry.Extensions.csproj | 2 +- .../EventSourcing.Backbone.WebEventTest.csproj | 2 +- .../Tests.Events.ConsumerWebTest.Service/Program.cs | 4 +--- .../Tests.Events.ConsumerWebTest.Service.csproj | 2 +- .../Tests.Events.ProducerWebTest.Service/Program.cs | 3 +-- .../Tests.Events.ProducerWebTest.Service.csproj | 2 +- Tests/WebSampleS3/WebSampleS3.csproj | 5 ++--- 13 files changed, 17 insertions(+), 27 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj index f82ec8f1..7eccb988 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/EventSourcing.Backbone.Channels.RedisConsumerProvider.csproj @@ -20,7 +20,7 @@ - + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 6e7c09e8..8086fcdb 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -473,6 +473,7 @@ async Task ReadBatchAsync() catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { + logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, @@ -480,20 +481,12 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( } catch (RedisServerException ex) { - logger?.LogWarning(ex, ex.Message); + logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, logger); } - //catch (Exception ex) - //{ - // logger?.LogWarning(ex, ex.Message); - // //await _connFactory.CreateConsumerGroupIfNotExistsAsync( - // // key, - // // plan.ConsumerGroup, - // // logger); - //} #endregion // Exception Handling } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj index ccdc75dd..c4880379 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/EventSourcing.Backbone.Channels.RedisProvider.Common.csproj @@ -16,7 +16,7 @@ - + diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index f1270c9b..f891aa7b 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj index ae19406e..d46bfd97 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj +++ b/Consumers/EventSourcing.Backbone.Consumers/EventSourcing.Backbone.Consumers.csproj @@ -24,7 +24,7 @@ - + diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index ba0bc0bd..e4795dbc 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -19,9 +19,10 @@ - + + - + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index fb339b5e..ddbc4429 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -18,7 +18,7 @@ - + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index d57e431a..95c36f2d 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -19,7 +19,7 @@ - + diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs index cd49b5c6..a9eaeb69 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Program.cs @@ -12,8 +12,6 @@ IWebHostEnvironment environment = builder.Environment; -string env = environment.EnvironmentName; - services.AddOpenTelemetry() .WithEventSourcingTracing(environment) .WithEventSourcingMetrics(environment); @@ -66,6 +64,6 @@ var logger = app.Services.GetService>(); List switches = new(); switches.Add("Consumer"); -logger.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); +logger?.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); app.Run(); diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj index ac7e95a4..01586060 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj @@ -10,7 +10,7 @@ - + diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs index 37a9f1b8..a4db8db4 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Program.cs @@ -11,7 +11,6 @@ IWebHostEnvironment environment = builder.Environment; -string env = environment.EnvironmentName; services.AddOpenTelemetry() .WithEventSourcingTracing(environment) @@ -64,6 +63,6 @@ var logger = app.Services.GetService>(); List switches = new(); switches.Add("Producer"); -logger.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); +logger?.LogInformation("Service Configuration Event Sourcing `{event-bundle}` on URI: `{URI}`, Features: [{features}]", "ProductCycle", ProductCycleConstants.URI, string.Join(", ", switches)); app.Run(); diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj index 4044a827..3e6fe116 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj @@ -10,7 +10,7 @@ - + diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index e81e8537..4882dd76 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -7,7 +7,7 @@ - + @@ -19,7 +19,7 @@ - + @@ -34,7 +34,6 @@ - From e56f316f091f96beea04b226c6c6de40e020fda9 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:52:43 +0300 Subject: [PATCH 163/178] test: base class --- .../AckPatternsTests.cs | 78 +----------------- .../DeleteKeysTests.cs | 58 +++++++------ .../EndToEndExplicitTests.cs | 20 ++++- .../EndToEndStressTests.cs | 78 +----------------- .../EndToEndTests.cs | 81 +------------------ .../InheritanceTests.cs | 78 +----------------- .../MigrationTest.cs | 79 +----------------- .../TestsBase.cs | 61 +++++++------- 8 files changed, 100 insertions(+), 433 deletions(-) diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs index 23b9e5c5..ff9015cc 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs @@ -28,16 +28,15 @@ namespace EventSourcing.Backbone.Tests /// /// The end to end tests. /// - public class AckPatternsTests : IDisposable + public class AckPatternsTests : TestsBase { - private readonly ITestOutputHelper _outputHelper; private readonly ISequenceOperationsConsumer _subscriber = A.Fake(); private readonly SequenceOperationsConsumerBridge _subscriberBridge; private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"test"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; @@ -55,8 +54,8 @@ public AckPatternsTests( ITestOutputHelper outputHelper, Func? producerChannelBuilder = null, Func? consumerChannelBuilder = null) + : base(outputHelper) { - _outputHelper = outputHelper; _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; @@ -359,76 +358,5 @@ private static CancellationToken GetCancellationToken() } #endregion // GetCancellationToken - - #region Dispose pattern - - - ~AckPatternsTests() - { - Dispose(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - try - { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( - logger: _fakeLogger, - configurationHook: cfg => cfg.AllowAdmin = true).Result; - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{URI}*"); - IDatabaseAsync db = conn.GetDatabase(); - - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. -#pragma warning disable CS8604 // Possible null reference argument. - foreach (string key in keys) - { - ab.Post(key); - } -#pragma warning restore CS8604 // Possible null reference argument. -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - - ab.Complete(); - ab.Completion.Wait(); - - async Task LocalAsync(string k) - { - try - { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - - #endregion // Dispose pattern } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs index e8325497..c72e658c 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/DeleteKeysTests.cs @@ -31,40 +31,46 @@ public async Task DELETE_KEYS_TEST(string pattern) { IConnectionMultiplexer conn = await RedisClientFactory.CreateProviderAsync( configurationHook: cfg => cfg.AllowAdmin = true); - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: pattern).ToArray(); - IDatabaseAsync db = conn.GetDatabase(); - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); - foreach (string? key in keys) + string serverNames = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; + foreach (var serverName in serverNames.Split(',')) { - if (string.IsNullOrWhiteSpace(key)) continue; - ab.Post(key); - } - - ab.Complete(); - await ab.Completion; + var server = conn.GetServer(serverName); + if (!server.IsConnected) + continue; + IEnumerable keys = server.Keys(pattern: pattern).ToArray(); + IDatabaseAsync db = conn.GetDatabase(); - async Task LocalAsync(string k) - { - try + var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); + foreach (string? key in keys) { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - Trace.WriteLine(k); + if (string.IsNullOrWhiteSpace(key)) continue; + ab.Post(key); } - #region Exception Handling - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) + ab.Complete(); + await ab.Completion; + + async Task LocalAsync(string k) { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } + try + { + await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); + Trace.WriteLine(k); + } + #region Exception Handling - #endregion // Exception Handling + catch (RedisTimeoutException ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + catch (Exception ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + + #endregion // Exception Handling + } } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index 6e17e650..d0bcd81f 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -91,9 +91,12 @@ public EndToEndExplicitTests( #region OnSucceed_ACK_Test - [Fact(Timeout = TIMEOUT)] + [Fact] + //[Fact(Timeout = TIMEOUT)] public async Task OnSucceed_ACK_Test() { + var sw = Stopwatch.StartNew(); + #region IEventFlow producer = ... IEventFlowProducer producer = _producerBuilder @@ -105,8 +108,16 @@ public async Task OnSucceed_ACK_Test() #endregion // IEventFlow producer = ... + var snapshot = sw.Elapsed; + _outputHelper.WriteLine($"Build producer = {snapshot:mm\\:ss\\.ff}"); + snapshot = sw.Elapsed; + await SendSequenceAsync(producer); + snapshot = sw.Elapsed - snapshot; + _outputHelper.WriteLine($"Produce = {snapshot:mm\\:ss\\.ff}"); + snapshot = sw.Elapsed; + var consumerOptions = new ConsumerOptions { AckBehavior = AckBehavior.OnSucceed, @@ -129,8 +140,15 @@ public async Task OnSucceed_ACK_Test() #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) + snapshot = sw.Elapsed - snapshot; + _outputHelper.WriteLine($"Build Consumer = {snapshot:mm\\:ss\\.ff}"); + snapshot = sw.Elapsed; + await subscription.Completion; + snapshot = sw.Elapsed - snapshot; + _outputHelper.WriteLine($"Consumed = {snapshot:mm\\:ss\\.ff}"); + #region Validation A.CallTo(() => _subscriber.Stage1Async(A.Ignored, A.Ignored, A.Ignored)) diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 5f26c45c..a87ae54c 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -22,15 +22,14 @@ namespace EventSourcing.Backbone.Tests { - public class EndToEndStressTests : IDisposable + public class EndToEndStressTests : TestsBase { - private readonly ITestOutputHelper _outputHelper; private readonly ISequenceOperationsConsumer _subscriber = A.Fake(); private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerHooksBuilder _consumerBuilder; private readonly string ENV = $"test"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1000 * 30; @@ -41,8 +40,8 @@ public EndToEndStressTests( ITestOutputHelper outputHelper, Func? producerChannelBuilder = null, Func? consumerChannelBuilder = null) + : base(outputHelper) { - _outputHelper = outputHelper; _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; @@ -207,76 +206,5 @@ private static CancellationToken GetCancellationToken() } #endregion // GetCancellationToken - - #region Dispose pattern - - - ~EndToEndStressTests() - { - Dispose(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - try - { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( - logger: _fakeLogger, - configurationHook: cfg => cfg.AllowAdmin = true) - .Result; - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{URI}*"); - IDatabaseAsync db = conn.GetDatabase(); - - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); - foreach (string? key in keys) - { - if (string.IsNullOrEmpty(key)) continue; - ab.Post(key); - } - - ab.Complete(); - ab.Completion.Wait(); - - async Task LocalAsync(string k) - { - try - { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - - - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - - #endregion // Dispose pattern } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 61166d5c..b31e7170 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -30,9 +30,8 @@ namespace EventSourcing.Backbone.Tests /// /// The end to end tests. /// - public class EndToEndTests : IDisposable + public class EndToEndTests : TestsBase { - private readonly ITestOutputHelper _outputHelper; private readonly ISequenceOperationsConsumer _subscriber = A.Fake(); private readonly SequenceOperationsConsumerBridge _subscriberBridge; private readonly ISequenceOperationsConsumer _autoSubscriber = A.Fake(); @@ -47,7 +46,7 @@ public class EndToEndTests : IDisposable private readonly IEventFlowStage2Consumer _stage2Consumer = A.Fake(); private readonly string ENV = $"test"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; @@ -65,8 +64,8 @@ public EndToEndTests( ITestOutputHelper outputHelper, Func? producerChannelBuilder = null, Func? consumerChannelBuilder = null) + : base(outputHelper) { - _outputHelper = outputHelper; _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; @@ -302,12 +301,11 @@ public async Task PartialConsumer_Allow_Test() for (int i = 0; i < times; i++) { - var p = new Person(100, "bnaya"); + var p = new Person(100 + i, "bnaya"); _outputHelper.WriteLine($"Cycle {i}"); await producer.Stage1Async(p, "ABC"); await producer.Stage2Async(p.ToJson(), "ABC".ToJson()); - } CancellationToken cancellation = GetCancellationToken(); @@ -1414,76 +1412,5 @@ private static CancellationToken GetCancellationToken() } #endregion // GetCancellationToken - - #region Dispose pattern - - - ~EndToEndTests() - { - Dispose(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - try - { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( - logger: _fakeLogger, - configurationHook: cfg => cfg.AllowAdmin = true).Result; - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{URI}*"); - IDatabaseAsync db = conn.GetDatabase(); - - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. -#pragma warning disable CS8604 // Possible null reference argument. - foreach (string key in keys) - { - ab.Post(key); - } -#pragma warning restore CS8604 // Possible null reference argument. -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - - ab.Complete(); - ab.Completion.Wait(); - - async Task LocalAsync(string k) - { - try - { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - - #endregion // Dispose pattern } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index 61d58b0b..cd001fad 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -27,9 +27,8 @@ namespace EventSourcing.Backbone.Tests /// /// The end to end tests. /// - public class InheritanceTests : IDisposable + public class InheritanceTests : TestsBase { - private readonly ITestOutputHelper _outputHelper; private readonly IFlowAConsumer _subscriberA = A.Fake(); private readonly IFlowBConsumer _subscriberB = A.Fake(); private readonly IFlowABConsumer _subscriberAB = A.Fake(); @@ -38,7 +37,7 @@ public class InheritanceTests : IDisposable private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"test"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private const int TIMEOUT = 1_000 * 50; @@ -55,8 +54,8 @@ public InheritanceTests( ITestOutputHelper outputHelper, Func? producerChannelBuilder = null, Func? consumerChannelBuilder = null) + :base(outputHelper) { - _outputHelper = outputHelper; _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); _producerBuilder = producerChannelBuilder?.Invoke(_producerBuilder, _fakeLogger) ?? _producerBuilder; @@ -301,76 +300,5 @@ private static CancellationToken GetCancellationToken(int duration = 10) } #endregion // GetCancellationToken - - #region Dispose pattern - - - ~InheritanceTests() - { - Dispose(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - try - { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( - logger: _fakeLogger, - configurationHook: cfg => cfg.AllowAdmin = true).Result; - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{URI}*"); - IDatabaseAsync db = conn.GetDatabase(); - - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. -#pragma warning disable CS8604 // Possible null reference argument. - foreach (string key in keys) - { - ab.Post(key); - } -#pragma warning restore CS8604 // Possible null reference argument. -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - - ab.Complete(); - ab.Completion.Wait(); - - async Task LocalAsync(string k) - { - try - { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - - #endregion // Dispose pattern } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 71a453de..105be70d 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -24,15 +24,14 @@ namespace EventSourcing.Backbone.Tests /// /// The end to end tests. /// - public class MigrationTest : IDisposable + public class MigrationTest : TestsBase { - private readonly ITestOutputHelper _outputHelper; private readonly ISequenceOperationsConsumer _subscriber = A.Fake(); private readonly SequenceOperationsConsumerBridge _subscriberBridge; private readonly IProducerStoreStrategyBuilder _producerBuilder; private readonly IConsumerStoreStrategyBuilder _consumerBuilder; private readonly string ENV = $"Development"; - private readonly string URI = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; + protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; @@ -45,9 +44,8 @@ public class MigrationTest : IDisposable ///
/// The output helper. public MigrationTest( - ITestOutputHelper outputHelper) + ITestOutputHelper outputHelper) : base(outputHelper) { - _outputHelper = outputHelper; _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); var stg = new RedisConsumerChannelSetting @@ -256,76 +254,5 @@ private static CancellationToken GetCancellationToken() } #endregion // GetCancellationToken - - #region Dispose pattern - - - ~MigrationTest() - { - Dispose(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - try - { - IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( - logger: _fakeLogger, - configurationHook: cfg => cfg.AllowAdmin = true).Result; - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{URI}*"); - IDatabaseAsync db = conn.GetDatabase(); - - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. -#pragma warning disable CS8604 // Possible null reference argument. - foreach (string key in keys) - { - ab.Post(key); - } -#pragma warning restore CS8604 // Possible null reference argument. -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - - ab.Complete(); - ab.Completion.Wait(); - - async Task LocalAsync(string k) - { - try - { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling - } - - #endregion // Dispose pattern } } diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs index 7c9aee56..0ddace58 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs @@ -57,43 +57,48 @@ public void Dispose() IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( logger: _fakeLogger, configurationHook: cfg => cfg.AllowAdmin = true).Result; - string serverName = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; - var server = conn.GetServer(serverName); - IEnumerable keys = server.Keys(pattern: $"*{URI}*"); - IDatabaseAsync db = conn.GetDatabase(); + string serverNames = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; + foreach (var serverName in serverNames.Split(',')) + { + var server = conn.GetServer(serverName); + if (!server.IsConnected) + continue; + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); + IDatabaseAsync db = conn.GetDatabase(); - var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); + var ab = new ActionBlock(k => LocalAsync(k), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning disable CS8604 // Possible null reference argument. - foreach (string key in keys) - { - ab.Post(key); - } + foreach (string key in keys) + { + ab.Post(key); + } #pragma warning restore CS8604 // Possible null reference argument. #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - ab.Complete(); - ab.Completion.Wait(); - - async Task LocalAsync(string k) - { - try - { - await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); - _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); - } - #region Exception Handling + ab.Complete(); + ab.Completion.Wait(); - catch (RedisTimeoutException ex) + async Task LocalAsync(string k) { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + try + { + await db.KeyDeleteAsync(k, CommandFlags.DemandMaster); + _outputHelper.WriteLine($"Cleanup: delete key [{k}]"); + } + #region Exception Handling + + catch (RedisTimeoutException ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + catch (Exception ex) + { + _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); + } + + #endregion // Exception Handling } - catch (Exception ex) - { - _outputHelper.WriteLine($"Test dispose timeout error (delete keys) {ex.FormatLazy()}"); - } - - #endregion // Exception Handling } } #region Exception Handling From 3de1c7eff2aa2719a0aa792594cb17e4ae91dafe Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:52:58 +0300 Subject: [PATCH 164/178] test: console test --- Tests/ConsoleTest/ConsoleTest.csproj | 38 +++++ Tests/ConsoleTest/IFoo.cs | 18 +++ Tests/ConsoleTest/Program.cs | 150 ++++++++++++++++++ .../Properties/launchSettings.json | 16 ++ Tests/ConsoleTest/icon.png | Bin 0 -> 58881 bytes 5 files changed, 222 insertions(+) create mode 100644 Tests/ConsoleTest/ConsoleTest.csproj create mode 100644 Tests/ConsoleTest/IFoo.cs create mode 100644 Tests/ConsoleTest/Program.cs create mode 100644 Tests/ConsoleTest/Properties/launchSettings.json create mode 100644 Tests/ConsoleTest/icon.png diff --git a/Tests/ConsoleTest/ConsoleTest.csproj b/Tests/ConsoleTest/ConsoleTest.csproj new file mode 100644 index 00000000..db61fa8b --- /dev/null +++ b/Tests/ConsoleTest/ConsoleTest.csproj @@ -0,0 +1,38 @@ + + + + Debug;Release;Gen + false + Exe + + + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ConsoleTest/IFoo.cs b/Tests/ConsoleTest/IFoo.cs new file mode 100644 index 00000000..92a7b830 --- /dev/null +++ b/Tests/ConsoleTest/IFoo.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using EventSourcing.Backbone; + +namespace ConsoleTest; + +[EventsContract(EventsContractType.Producer)] +[EventsContract(EventsContractType.Consumer)] +public interface IFoo +{ + ValueTask Event1Async(); + ValueTask Event2Async(int i); + ValueTask Event3Async(string message); +} diff --git a/Tests/ConsoleTest/Program.cs b/Tests/ConsoleTest/Program.cs new file mode 100644 index 00000000..e414efbc --- /dev/null +++ b/Tests/ConsoleTest/Program.cs @@ -0,0 +1,150 @@ +#pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation + +using EventSourcing.Backbone; +using System; +using System.Diagnostics; +using System.Text.Json; +using EventSourcing.Backbone.Building; +using Microsoft.Extensions.Logging; +using StackExchange.Redis; + +using ConsoleTest; +using FakeItEasy; +using System.Threading.Tasks.Dataflow; +using EventSourcing.Backbone.Enums; + +const int MAX = 8_000; +CancellationTokenSource cancellation = new CancellationTokenSource( + Debugger.IsAttached + ? TimeSpan.FromMinutes(10) + : TimeSpan.FromSeconds(400)); + +string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; + +string URI = $"console-{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}"; + +ILogger fakeLogger = A.Fake(); +IFooConsumer subscriber = A.Fake(); + +await Cleanup(END_POINT_KEY, URI, fakeLogger); + +Prepare(subscriber); + + + + +Console.WriteLine("Console Test"); + +string ENV = $"console-test"; + +var sw = Stopwatch.StartNew(); + +IFooProducer producer = ProducerBuilder.Empty.UseRedisChannel() + .Environment(ENV) + .Uri(URI) + .BuildFooProducer(); + +var snapshot = sw.Elapsed; +Console.WriteLine($"Build producer = {snapshot:mm\\:ss\\.ff}"); +snapshot = sw.Elapsed; + +var ab = new ActionBlock(async i => +{ + await producer.Event1Async(); + await producer.Event2Async(i); + await producer.Event3Async($"e-{1}"); +}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 }); +for (int i = 0; i < MAX; i++) +{ + ab.Post(i); + //await producer.Event1Async(); + //await producer.Event2Async(i); + //await producer.Event3Async($"e-{1}"); +} +ab.Complete(); +await ab.Completion; + +snapshot = sw.Elapsed - snapshot; +Console.WriteLine($"Produce = {snapshot:mm\\:ss\\.ff}"); +snapshot = sw.Elapsed; + +var consumerOptions = new ConsumerOptions +{ + AckBehavior = AckBehavior.OnSucceed, + PartialBehavior = PartialConsumerBehavior.Loose, + MaxMessages = MAX * 3 /* detach consumer after 2 messages*/ +}; + +IConsumerSubscribeBuilder builder = ConsumerBuilder.Empty.UseRedisChannel() + .WithOptions(o => consumerOptions) + .WithCancellation(cancellation.Token) + .Environment(ENV) + .Uri(URI); + +snapshot = sw.Elapsed - snapshot; +Console.WriteLine($"Build Consumer = {snapshot:mm\\:ss\\.ff}"); +snapshot = sw.Elapsed; + +await using IConsumerLifetime subscription = builder + .Group("CONSUMER_GROUP_1") + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") + .SubscribeFooConsumer(subscriber); + +await subscription.Completion; + +snapshot = sw.Elapsed - snapshot; +Console.WriteLine($"Consumed = {snapshot:mm\\:ss\\.ff}"); +snapshot = sw.Elapsed; + +try +{ + A.CallTo(() => subscriber.Event1Async(A.Ignored)) + .MustHaveHappened(MAX, Times.Exactly); + A.CallTo(() => subscriber.Event2Async(A.Ignored, A.Ignored)) + .MustHaveHappened(MAX, Times.Exactly); + A.CallTo(() => subscriber.Event3Async(A.Ignored, A.Ignored)) + .MustHaveHappened(MAX, Times.Exactly); +} +catch (Exception ex) +{ + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(ex.FormatLazy()); + Console.ResetColor(); +} + +Console.WriteLine("Done"); + +static async Task Cleanup(string END_POINT_KEY, string URI, ILogger fakeLogger) +{ + IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( + logger: fakeLogger, + configurationHook: cfg => cfg.AllowAdmin = true).Result; + string serverNames = Environment.GetEnvironmentVariable(END_POINT_KEY) ?? "localhost:6379"; + foreach (var serverName in serverNames.Split(',')) + { + var server = conn.GetServer(serverName); + if (!server.IsConnected) + continue; + IEnumerable keys = server.Keys(pattern: $"*{URI}*"); + IDatabaseAsync db = conn.GetDatabase(); + + var ab = new ActionBlock(k => db.KeyDeleteAsync(k, CommandFlags.DemandMaster), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); + foreach (string key in keys) + { + ab.Post(key); + } + + ab.Complete(); + await ab.Completion; + } +} + +static void Prepare(IFooConsumer subscriber) +{ + A.CallTo(() => subscriber.Event1Async(A.Ignored)) + .ReturnsLazily(() => ValueTask.CompletedTask); + A.CallTo(() => subscriber.Event2Async(A.Ignored, A.Ignored)) + .ReturnsLazily(() => ValueTask.CompletedTask); + A.CallTo(() => subscriber.Event3Async(A.Ignored, A.Ignored)) + .ReturnsLazily(() => ValueTask.CompletedTask); +} \ No newline at end of file diff --git a/Tests/ConsoleTest/Properties/launchSettings.json b/Tests/ConsoleTest/Properties/launchSettings.json new file mode 100644 index 00000000..0cd18a71 --- /dev/null +++ b/Tests/ConsoleTest/Properties/launchSettings.json @@ -0,0 +1,16 @@ +{ + "profiles": { + "ConsoleTest": { + "commandName": "Project", + "environmentVariables": { + "REDIS_EVENT_SOURCE_ENDPOINT": "localhost:6379" + } + }, + "WSL": { + "commandName": "Project", + "environmentVariables": { + "REDIS_EVENT_SOURCE_ENDPOINT": "localhost:6379" + } + } + } +} \ No newline at end of file diff --git a/Tests/ConsoleTest/icon.png b/Tests/ConsoleTest/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17d68338d22026745615d9d8d26d21672aff093d GIT binary patch literal 58881 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

9`^J#0udjOm`|0!d09o6%f)G*< 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 Date: Mon, 19 Jun 2023 13:54:45 +0300 Subject: [PATCH 165/178] rfc: add overload (internal) --- .editorconfig | 2 + .../RedisConsumerChannel.cs | 15 +++++-- .../RedisCommonProviderExtensions.cs | 7 +++- ...ensions.cs => RedisTelemetryExtensions.cs} | 2 +- .../ConsumerBase.EventSourceSubscriber.cs | 40 +++++++++++++------ .../Builder/ConsumerBuilder.cs | 14 +++++++ .../IConsumerSubscribtionHubBuilder.cs | 11 +++++ EventSourcing.Backbone.sln | 8 ++++ .../Concrete/BridgeIncrementalGenerator.cs | 14 +++++++ 9 files changed, 93 insertions(+), 20 deletions(-) rename Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/{RedisTelemetryrExtensions.cs => RedisTelemetryExtensions.cs} (95%) diff --git a/.editorconfig b/.editorconfig index 81030afd..7c692a83 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,4 @@ [*.cs] +# HAA0101: Array allocation for params parameter +dotnet_diagnostic.HAA0101.severity = none diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 8086fcdb..4f293311 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -249,6 +249,10 @@ async ValueTask HandleBatchAsync() return await policy.ExecuteAsync(HandleBatchBreakerAsync, cancellationToken); } + #endregion // HandleBatchAsync + + + #region HandleBatchBreakerAsync async Task HandleBatchBreakerAsync(CancellationToken ct) { @@ -266,16 +270,18 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) } ct.ThrowIfCancellationRequested(); - + try { var batchCancellation = new CancellationTokenSource(); int i = 0; batchCancellation.Token.Register(async () => { + // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); await ReleaseAsync(freeTargets); }); + // TODO: [bnaya 2023-06-19] enable parallel consuming (when order doesn't matters) See #RELEASE for (; i < results.Length && !batchCancellation.IsCancellationRequested; i++) { StreamEntry result = results[i]; @@ -419,11 +425,12 @@ IEnumerable ExtractTraceContext(Dictionary entri } else { + // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) { RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); - await ReleaseAsync(freeTargets); - await Task.Delay(1000, ct); + await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet + await Task.Delay(releaseDelay, ct); } } } @@ -435,7 +442,7 @@ IEnumerable ExtractTraceContext(Dictionary entri return true; } - #endregion // HandleBatchAsync + #endregion // HandleBatchBreakerAsync #region ReadBatchAsync diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index df9e821b..297cafb0 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -112,10 +112,13 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( try { using var lk = await _lock.AcquireAsync(); - await db.StreamCreateConsumerGroupAsync(eventSourceKey, + if (await db.StreamCreateConsumerGroupAsync(eventSourceKey, consumerGroup, StreamPosition.Beginning, - flags: CommandFlags.DemandMaster); + flags: CommandFlags.DemandMaster)) + { + break; + } } #region Exception Handling diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs similarity index 95% rename from Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs rename to Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs index 200332e8..a1046369 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryrExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs @@ -2,7 +2,7 @@ namespace EventSourcing.Backbone; -public static class RedisTelemetryrExtensions +public static class RedisTelemetryExtensions { #region InjectTelemetryTags diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index 5759753e..74ffb95f 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System.Collections; +using System.Collections.Concurrent; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Enums; @@ -190,17 +191,17 @@ private async ValueTask ConsumingAsync( if (behavior == AckBehavior.OnSucceed) await ack.AckAsync(); } - else - { - if (partialBehavior == PartialConsumerBehavior.Loose) - { - await ack.AckAsync(); - } - if (partialBehavior == PartialConsumerBehavior.Sequential) - { - await ack.AckAsync(); - } - } + //else + //{ + // if (partialBehavior == PartialConsumerBehavior.Loose) + // { + // await ack.AckAsync(); + // } + // if (partialBehavior == PartialConsumerBehavior.Sequential) + // { + // await ack.AckAsync(); + // } + //} } } #region Exception Handling @@ -227,7 +228,6 @@ private async ValueTask ConsumingAsync( #endregion // Exception Handling finally { - if (Plan.Options.AckBehavior == AckBehavior.OnFinally && partialBehavior != PartialConsumerBehavior.Sequential) { await ack.AckAsync(); @@ -250,6 +250,20 @@ private async ValueTask ConsumingAsync( #region Subscribe + ///

+ /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// The subscription lifetime (dispose to remove the subscription) + /// + /// _plan + IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge handler) + + { + return ((IConsumerSubscribtionHubBuilder)this).Subscribe(handler.ToEnumerable()); + } + /// /// Subscribe consumer. /// diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs index 7071af2d..9087f0d0 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBuilder.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Collections; using System.Diagnostics; using System.Text; using System.Text.Json; @@ -411,6 +412,19 @@ IConsumerSubscribeBuilder IConsumerSubscribeBuilder.Name(string consumerName) #region Subscribe + /// + /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// The subscription lifetime (dispose to remove the subscription) + /// + IConsumerLifetime IConsumerSubscribtionHubBuilder.Subscribe(ISubscriptionBridge handler) + + { + return ((IConsumerSubscribtionHubBuilder)this).Subscribe(handler.ToEnumerable()); + } + /// /// Subscribe consumer. /// diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribtionHubBuilder.cs b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribtionHubBuilder.cs index 4466d989..3299add5 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribtionHubBuilder.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Builder/Building/IConsumerSubscribtionHubBuilder.cs @@ -2,6 +2,17 @@ { public interface IConsumerSubscribtionHubBuilder { + /// + /// Subscribe consumer. + /// + /// Per operation invocation handler, handle methods calls. + /// + /// Remove subscription. + /// keeping the disposable will prevent the consumer to be collected + /// by th GC (when the behavior don't indicate to hook it until cancellation or dispose). + /// + IConsumerLifetime Subscribe(ISubscriptionBridge handler); + /// /// Subscribe consumer. /// diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 4559a248..f913f4da 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -81,6 +81,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Events.ProducerWebTes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Events.ConsumerWebTest.Service", "Tests\Specialized\Tests.Events.ConsumerWebTest.Service\Tests.Events.ConsumerWebTest.Service.csproj", "{842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTest", "Tests\ConsoleTest\ConsoleTest.csproj", "{EAE6F008-AB9E-4321-BC31-D50534111971}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -209,6 +211,11 @@ Global {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Gen|Any CPU.ActiveCfg = Gen|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Release|Any CPU.ActiveCfg = Release|Any CPU {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F}.Release|Any CPU.Build.0 = Release|Any CPU + {EAE6F008-AB9E-4321-BC31-D50534111971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAE6F008-AB9E-4321-BC31-D50534111971}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAE6F008-AB9E-4321-BC31-D50534111971}.Gen|Any CPU.ActiveCfg = Gen|Any CPU + {EAE6F008-AB9E-4321-BC31-D50534111971}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAE6F008-AB9E-4321-BC31-D50534111971}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -240,6 +247,7 @@ Global {8E90962C-870D-49C6-A260-585D16F13614} = {DB106708-EA65-4FD3-9362-950CA07DB2E6} {ED906FBD-F8BB-40D5-8629-00CE0F963681} = {DB106708-EA65-4FD3-9362-950CA07DB2E6} {842CEAED-FA0D-4EFF-805A-D37F62AEFB0F} = {DB106708-EA65-4FD3-9362-950CA07DB2E6} + {EAE6F008-AB9E-4321-BC31-D50534111971} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {103E4CA1-859C-4E6A-9F2D-7FD54A8E687C} diff --git a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs index 5017060c..7819c328 100644 --- a/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs +++ b/src-gen/EventSourcing.Backbone.SrcGen/Generators/Concrete/BridgeIncrementalGenerator.cs @@ -91,6 +91,20 @@ protected GenInstruction OnGenerateConsumerBridgeExtensions( builder.AppendLine($"\tpublic static class {fileName}"); builder.AppendLine("\t{"); + builder.AppendLine("\t\t/// "); + builder.AppendLine($"\t\t/// Subscribe to {interfaceName}"); + builder.AppendLine("\t\t/// "); + builder.AppendLine("\t\t/// The builder."); + builder.AppendLine("\t\t/// The targets handler."); + builder.AppendLine($"\t\tpublic static IConsumerLifetime Subscribe{prefix}("); + builder.AppendLine("\t\t\t\tthis IConsumerSubscribtionHubBuilder source,"); + builder.AppendLine($"\t\t\t\t{interfaceName} target)"); + builder.AppendLine("\t\t{"); + builder.AppendLine($"\t\t\tvar bridge = new {bridge}(target);"); + builder.AppendLine("\t\t\treturn source.Subscribe(bridge);"); + builder.AppendLine("\t\t}"); + builder.AppendLine(); + builder.AppendLine("\t\t/// "); builder.AppendLine($"\t\t/// Subscribe to {interfaceName}"); builder.AppendLine("\t\t/// "); From efcf712908c6501b17995b3a9c3d94c7b3494c57 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:03:17 +0300 Subject: [PATCH 166/178] test: add logging --- Tests/ConsoleTest/ConsoleTest.csproj | 1 + Tests/ConsoleTest/Program.cs | 90 ++++++++++++--------- Tests/HelloWorld/ProducingEvents/Program.cs | 1 - 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/Tests/ConsoleTest/ConsoleTest.csproj b/Tests/ConsoleTest/ConsoleTest.csproj index db61fa8b..1b1cd0bd 100644 --- a/Tests/ConsoleTest/ConsoleTest.csproj +++ b/Tests/ConsoleTest/ConsoleTest.csproj @@ -15,6 +15,7 @@ + diff --git a/Tests/ConsoleTest/Program.cs b/Tests/ConsoleTest/Program.cs index e414efbc..add53676 100644 --- a/Tests/ConsoleTest/Program.cs +++ b/Tests/ConsoleTest/Program.cs @@ -1,4 +1,5 @@ #pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation +#pragma warning disable HAA0301 // Closure Allocation Source using EventSourcing.Backbone; using System; @@ -6,14 +7,17 @@ using System.Text.Json; using EventSourcing.Backbone.Building; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; using StackExchange.Redis; using ConsoleTest; using FakeItEasy; using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Enums; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; -const int MAX = 8_000; +const int MAX = 1_000; CancellationTokenSource cancellation = new CancellationTokenSource( Debugger.IsAttached ? TimeSpan.FromMinutes(10) @@ -23,29 +27,59 @@ string URI = $"console-{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}"; -ILogger fakeLogger = A.Fake(); +string ENV = $"console-test"; + IFooConsumer subscriber = A.Fake(); -await Cleanup(END_POINT_KEY, URI, fakeLogger); +var services = new ServiceCollection(); +services.AddLogging(b => b.AddConsole()); +services.AddEventSourceRedisConnection(); +services.AddSingleton(ioc => +{ + IFooProducer producer = ioc.ResolveRedisProducerChannel() + .Environment(ENV) + .Uri(URI) + .BuildFooProducer(); + return producer; +}); +services.AddSingleton(ioc => +{ + var consumerOptions = new ConsumerOptions + { + AckBehavior = AckBehavior.OnSucceed, + PartialBehavior = PartialConsumerBehavior.Loose, + MaxMessages = MAX * 3 /* detach consumer after 2 messages*/ + }; + IConsumerLifetime subscription = + ioc.ResolveRedisConsumerChannel() + .WithOptions(o => consumerOptions) + .WithCancellation(cancellation.Token) + .Environment(ENV) + .Uri(URI) + .Group("CONSUMER_GROUP_1") + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") + .SubscribeFooConsumer(subscriber); + return subscription; +}); -Prepare(subscriber); +var sp = services.BuildServiceProvider(); +ILogger logger = sp.GetService>() ?? throw new NullReferenceException(); +logger.LogInformation("Console Test"); +await Cleanup(END_POINT_KEY, URI, logger); +Prepare(subscriber); -Console.WriteLine("Console Test"); -string ENV = $"console-test"; +IFooProducer producer = sp.GetService() ?? throw new NullReferenceException(); +IConsumerLifetime subscription = sp.GetService() ?? throw new NullReferenceException(); -var sw = Stopwatch.StartNew(); -IFooProducer producer = ProducerBuilder.Empty.UseRedisChannel() - .Environment(ENV) - .Uri(URI) - .BuildFooProducer(); +var sw = Stopwatch.StartNew(); var snapshot = sw.Elapsed; -Console.WriteLine($"Build producer = {snapshot:mm\\:ss\\.ff}"); +logger.LogInformation($"Build producer = {snapshot:mm\\:ss\\.ff}"); snapshot = sw.Elapsed; var ab = new ActionBlock(async i => @@ -53,7 +87,7 @@ await producer.Event1Async(); await producer.Event2Async(i); await producer.Event3Async($"e-{1}"); -}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 }); +}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 }); for (int i = 0; i < MAX; i++) { ab.Post(i); @@ -65,35 +99,13 @@ await ab.Completion; snapshot = sw.Elapsed - snapshot; -Console.WriteLine($"Produce = {snapshot:mm\\:ss\\.ff}"); +logger.LogInformation($"Produce = {snapshot:mm\\:ss\\.ff}"); snapshot = sw.Elapsed; -var consumerOptions = new ConsumerOptions -{ - AckBehavior = AckBehavior.OnSucceed, - PartialBehavior = PartialConsumerBehavior.Loose, - MaxMessages = MAX * 3 /* detach consumer after 2 messages*/ -}; - -IConsumerSubscribeBuilder builder = ConsumerBuilder.Empty.UseRedisChannel() - .WithOptions(o => consumerOptions) - .WithCancellation(cancellation.Token) - .Environment(ENV) - .Uri(URI); - -snapshot = sw.Elapsed - snapshot; -Console.WriteLine($"Build Consumer = {snapshot:mm\\:ss\\.ff}"); -snapshot = sw.Elapsed; - -await using IConsumerLifetime subscription = builder - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .SubscribeFooConsumer(subscriber); - await subscription.Completion; snapshot = sw.Elapsed - snapshot; -Console.WriteLine($"Consumed = {snapshot:mm\\:ss\\.ff}"); +logger.LogInformation($"Consumed = {snapshot:mm\\:ss\\.ff}"); snapshot = sw.Elapsed; try @@ -108,11 +120,11 @@ catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(ex.FormatLazy()); + logger.LogInformation(ex.FormatLazy()); Console.ResetColor(); } -Console.WriteLine("Done"); +logger.LogInformation("Done"); static async Task Cleanup(string END_POINT_KEY, string URI, ILogger fakeLogger) { diff --git a/Tests/HelloWorld/ProducingEvents/Program.cs b/Tests/HelloWorld/ProducingEvents/Program.cs index e5e9110b..418dafc0 100644 --- a/Tests/HelloWorld/ProducingEvents/Program.cs +++ b/Tests/HelloWorld/ProducingEvents/Program.cs @@ -10,7 +10,6 @@ .BuildHelloEventsProducer(); - Console.Write("What is your name? "); string name = Console.ReadLine(); await producer.NameAsync(name ?? "Unknown"); From 7873ae98f07b166f22f39c891b633921a2122dab Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:08:01 +0300 Subject: [PATCH 167/178] feat: di into testing console --- ...g.Backbone.OpenTelemetry.Extensions.csproj | 4 ++ .../EventSourcingOtel.cs | 53 +++++++++++++----- Tests/ConsoleTest/ConsoleTest.csproj | 13 +++++ Tests/ConsoleTest/OpenTelemetryExtensions.cs | 54 +++++++++++++++++++ Tests/ConsoleTest/Program.cs | 4 ++ Tests/ConsoleTest/Worker.cs | 32 +++++++++++ 6 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 Tests/ConsoleTest/OpenTelemetryExtensions.cs create mode 100644 Tests/ConsoleTest/Worker.cs diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index ddbc4429..f26ab58e 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -23,6 +23,10 @@ + + + + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index aa0909b8..dcbe31d3 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Hosting; +using EventSourcing.Backbone; + +using Microsoft.Extensions.Hosting; using OpenTelemetry; using OpenTelemetry.Metrics; @@ -7,6 +9,10 @@ // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 +// see: +// https://opentelemetry.io/docs/instrumentation/net/getting-started/ +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + namespace Microsoft.Extensions.DependencyInjection; @@ -40,22 +46,32 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( IHostEnvironment hostEnv, Action? injection = null) { - // see: - // https://opentelemetry.io/docs/instrumentation/net/getting-started/ - // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables - - var appName = hostEnv.ApplicationName; + var env = hostEnv.ApplicationName; + return builder.WithEventSourcingTracing(env, injection); + } + /// + /// Adds the open-telemetry tracing binding. + /// + /// The build. + /// The environment. + /// Enable to inject additional setting. + /// + public static OpenTelemetryBuilder WithEventSourcingTracing( + this OpenTelemetryBuilder builder, + Env env, + Action? injection = null) + { builder .WithTracing(tracerProviderBuilder => { - var sources = new[] { appName, + var sources = new[] { (string)env, REDIS_CONSUMER_CHANNEL_SOURCE, REDIS_PRODUCER_CHANNEL_SOURCE}; tracerProviderBuilder .AddSource(sources) - .ConfigureResource(resource => resource.AddService(appName)); + .ConfigureResource(resource => resource.AddService(env)); injection?.Invoke(tracerProviderBuilder); }); @@ -79,15 +95,26 @@ public static OpenTelemetryBuilder WithEventSourcingMetrics( IHostEnvironment hostEnv, Action? injection = null) { - // see: - // https://opentelemetry.io/docs/instrumentation/net/getting-started/ - // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + var env = hostEnv.ApplicationName; + return builder.WithEventSourcingMetrics(env, injection); + } - var appName = hostEnv.ApplicationName; + /// + /// Adds the open-telemetry metrics binding. + /// + /// The build. + /// The host environment. + /// The injection. + /// + public static OpenTelemetryBuilder WithEventSourcingMetrics( + this OpenTelemetryBuilder builder, + Env env, + Action? injection = null) + { builder.WithMetrics(metricsProviderBuilder => { metricsProviderBuilder - .ConfigureResource(resource => resource.AddService(appName)); + .ConfigureResource(resource => resource.AddService(env)); injection?.Invoke(metricsProviderBuilder); }); diff --git a/Tests/ConsoleTest/ConsoleTest.csproj b/Tests/ConsoleTest/ConsoleTest.csproj index 1b1cd0bd..129639fe 100644 --- a/Tests/ConsoleTest/ConsoleTest.csproj +++ b/Tests/ConsoleTest/ConsoleTest.csproj @@ -18,11 +18,24 @@ + + + + + + + + + + + + + diff --git a/Tests/ConsoleTest/OpenTelemetryExtensions.cs b/Tests/ConsoleTest/OpenTelemetryExtensions.cs new file mode 100644 index 00000000..c919cdde --- /dev/null +++ b/Tests/ConsoleTest/OpenTelemetryExtensions.cs @@ -0,0 +1,54 @@ +using EventSourcing.Backbone; + +using Microsoft.Extensions.DependencyInjection; + +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +// see: +// https://opentelemetry.io/docs/instrumentation/net/getting-started/ +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + +namespace ConsoleTest; + +/// +/// Open telemetry extensions for ASP.NET Core +/// +internal static class OpenTelemetryExtensions +{ + #region AddOpenTelemetryEventSourcing + + /// + /// Adds open telemetry for event sourcing. + /// + /// The services. + /// The environment. + /// + public static IServiceCollection AddOpenTelemetryEventSourcing(this IServiceCollection services, Env environment) + { + // see: + // https://opentelemetry.io/docs/instrumentation/net/getting-started/ + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + services.AddOpenTelemetry() + .WithEventSourcingTracing(environment, + cfg => + { + cfg.SetResourceBuilder(ResourceBuilder.CreateDefault() + .AddService("ConsoleTest")) + .AddOtlpExporter() + .AddConsoleExporter(); + }) + .WithEventSourcingMetrics(environment, cfg => + { + cfg + .AddOtlpExporter() + .AddPrometheusExporter() + .AddMeter("ConsoleTest"); + }); + + return services; + } + + #endregion // AddOpenTelemetryEventSourcing +} diff --git a/Tests/ConsoleTest/Program.cs b/Tests/ConsoleTest/Program.cs index add53676..63c8f731 100644 --- a/Tests/ConsoleTest/Program.cs +++ b/Tests/ConsoleTest/Program.cs @@ -61,6 +61,10 @@ .SubscribeFooConsumer(subscriber); return subscription; }); +services.AddOpenTelemetryEventSourcing(ENV); + +services.AddHostedService(); + var sp = services.BuildServiceProvider(); ILogger logger = sp.GetService>() ?? throw new NullReferenceException(); diff --git a/Tests/ConsoleTest/Worker.cs b/Tests/ConsoleTest/Worker.cs new file mode 100644 index 00000000..38c5ca8b --- /dev/null +++ b/Tests/ConsoleTest/Worker.cs @@ -0,0 +1,32 @@ +using EventSourcing.Backbone; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +// see: +// https://opentelemetry.io/docs/instrumentation/net/getting-started/ +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + +namespace ConsoleTest; + +/// +/// Open telemetry extensions for ASP.NET Core +/// +internal class Worker : BackgroundService +{ + private readonly ILogger _logger; + + public Worker(ILogger logger) + { + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + } +} From 20e2e184859117efca7209ded37715b0e4cdce83 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:09:00 +0300 Subject: [PATCH 168/178] feat: di into testing console --- Tests/ConsoleTest/Constants.cs | 12 ++ Tests/ConsoleTest/OpenTelemetryExtensions.cs | 4 +- Tests/ConsoleTest/Program.cs | 161 +++++-------------- Tests/ConsoleTest/Worker.cs | 84 +++++++++- 4 files changed, 141 insertions(+), 120 deletions(-) create mode 100644 Tests/ConsoleTest/Constants.cs diff --git a/Tests/ConsoleTest/Constants.cs b/Tests/ConsoleTest/Constants.cs new file mode 100644 index 00000000..830bf968 --- /dev/null +++ b/Tests/ConsoleTest/Constants.cs @@ -0,0 +1,12 @@ +using FakeItEasy; + +namespace ConsoleTest; + +internal static class Constants +{ + public const int MAX = 1_000; + public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; + public const string ENV = $"console-test"; + public static readonly IFooConsumer Subscriber = A.Fake(); + +} diff --git a/Tests/ConsoleTest/OpenTelemetryExtensions.cs b/Tests/ConsoleTest/OpenTelemetryExtensions.cs index c919cdde..5474d328 100644 --- a/Tests/ConsoleTest/OpenTelemetryExtensions.cs +++ b/Tests/ConsoleTest/OpenTelemetryExtensions.cs @@ -36,8 +36,8 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this IServiceColl { cfg.SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService("ConsoleTest")) - .AddOtlpExporter() - .AddConsoleExporter(); + .AddOtlpExporter(); + // .AddConsoleExporter(); }) .WithEventSourcingMetrics(environment, cfg => { diff --git a/Tests/ConsoleTest/Program.cs b/Tests/ConsoleTest/Program.cs index 63c8f731..1a6b759e 100644 --- a/Tests/ConsoleTest/Program.cs +++ b/Tests/ConsoleTest/Program.cs @@ -16,119 +16,56 @@ using EventSourcing.Backbone.Enums; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; +using static ConsoleTest.Constants; -const int MAX = 1_000; CancellationTokenSource cancellation = new CancellationTokenSource( Debugger.IsAttached ? TimeSpan.FromMinutes(10) : TimeSpan.FromSeconds(400)); - -string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; - string URI = $"console-{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}"; - -string ENV = $"console-test"; - -IFooConsumer subscriber = A.Fake(); - -var services = new ServiceCollection(); -services.AddLogging(b => b.AddConsole()); -services.AddEventSourceRedisConnection(); -services.AddSingleton(ioc => -{ - IFooProducer producer = ioc.ResolveRedisProducerChannel() - .Environment(ENV) - .Uri(URI) - .BuildFooProducer(); - return producer; -}); -services.AddSingleton(ioc => -{ - var consumerOptions = new ConsumerOptions - { - AckBehavior = AckBehavior.OnSucceed, - PartialBehavior = PartialConsumerBehavior.Loose, - MaxMessages = MAX * 3 /* detach consumer after 2 messages*/ - }; - IConsumerLifetime subscription = - ioc.ResolveRedisConsumerChannel() - .WithOptions(o => consumerOptions) - .WithCancellation(cancellation.Token) - .Environment(ENV) - .Uri(URI) - .Group("CONSUMER_GROUP_1") - .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") - .SubscribeFooConsumer(subscriber); - return subscription; -}); -services.AddOpenTelemetryEventSourcing(ENV); - -services.AddHostedService(); - - -var sp = services.BuildServiceProvider(); -ILogger logger = sp.GetService>() ?? throw new NullReferenceException(); - -logger.LogInformation("Console Test"); - -await Cleanup(END_POINT_KEY, URI, logger); - -Prepare(subscriber); - - -IFooProducer producer = sp.GetService() ?? throw new NullReferenceException(); -IConsumerLifetime subscription = sp.GetService() ?? throw new NullReferenceException(); - - -var sw = Stopwatch.StartNew(); - -var snapshot = sw.Elapsed; -logger.LogInformation($"Build producer = {snapshot:mm\\:ss\\.ff}"); -snapshot = sw.Elapsed; - -var ab = new ActionBlock(async i => -{ - await producer.Event1Async(); - await producer.Event2Async(i); - await producer.Event3Async($"e-{1}"); -}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 }); -for (int i = 0; i < MAX; i++) -{ - ab.Post(i); - //await producer.Event1Async(); - //await producer.Event2Async(i); - //await producer.Event3Async($"e-{1}"); -} -ab.Complete(); -await ab.Completion; - -snapshot = sw.Elapsed - snapshot; -logger.LogInformation($"Produce = {snapshot:mm\\:ss\\.ff}"); -snapshot = sw.Elapsed; - -await subscription.Completion; - -snapshot = sw.Elapsed - snapshot; -logger.LogInformation($"Consumed = {snapshot:mm\\:ss\\.ff}"); -snapshot = sw.Elapsed; - -try -{ - A.CallTo(() => subscriber.Event1Async(A.Ignored)) - .MustHaveHappened(MAX, Times.Exactly); - A.CallTo(() => subscriber.Event2Async(A.Ignored, A.Ignored)) - .MustHaveHappened(MAX, Times.Exactly); - A.CallTo(() => subscriber.Event3Async(A.Ignored, A.Ignored)) - .MustHaveHappened(MAX, Times.Exactly); -} -catch (Exception ex) -{ - Console.ForegroundColor = ConsoleColor.Red; - logger.LogInformation(ex.FormatLazy()); - Console.ResetColor(); -} - -logger.LogInformation("Done"); +IHostBuilder host = Host.CreateDefaultBuilder(args) + .ConfigureServices(services => + { + services.AddLogging(b => b.AddConsole()); + services.AddEventSourceRedisConnection(); + services.AddSingleton(cancellation); + services.AddSingleton(ioc => + { + IFooProducer producer = ioc.ResolveRedisProducerChannel() + .Environment(ENV) + .Uri(URI) + .BuildFooProducer(); + return producer; + }); + services.AddSingleton(ioc => + { + var consumerOptions = new ConsumerOptions + { + AckBehavior = AckBehavior.OnSucceed, + PartialBehavior = PartialConsumerBehavior.Loose, + MaxMessages = MAX * 3 /* detach consumer after 2 messages*/ + }; + IConsumerLifetime subscription = + ioc.ResolveRedisConsumerChannel() + .WithOptions(o => consumerOptions) + .WithCancellation(cancellation.Token) + .Environment(ENV) + .Uri(URI) + .Group("CONSUMER_GROUP_1") + .Name($"TEST {DateTime.UtcNow:HH:mm:ss}") + .SubscribeFooConsumer(Subscriber); + return subscription; + }); + services.AddOpenTelemetryEventSourcing(ENV); + + services.AddHostedService(); + }); + +IHost hosing = host.Build(); +ILogger _logger = hosing.Services.GetService>() ?? throw new NullReferenceException(); +await Cleanup(END_POINT_KEY, URI, _logger); + +await hosing.RunAsync(cancellation.Token); static async Task Cleanup(string END_POINT_KEY, string URI, ILogger fakeLogger) { @@ -153,14 +90,4 @@ static async Task Cleanup(string END_POINT_KEY, string URI, ILogger fakeLogger) ab.Complete(); await ab.Completion; } -} - -static void Prepare(IFooConsumer subscriber) -{ - A.CallTo(() => subscriber.Event1Async(A.Ignored)) - .ReturnsLazily(() => ValueTask.CompletedTask); - A.CallTo(() => subscriber.Event2Async(A.Ignored, A.Ignored)) - .ReturnsLazily(() => ValueTask.CompletedTask); - A.CallTo(() => subscriber.Event3Async(A.Ignored, A.Ignored)) - .ReturnsLazily(() => ValueTask.CompletedTask); } \ No newline at end of file diff --git a/Tests/ConsoleTest/Worker.cs b/Tests/ConsoleTest/Worker.cs index 38c5ca8b..f5fa14a6 100644 --- a/Tests/ConsoleTest/Worker.cs +++ b/Tests/ConsoleTest/Worker.cs @@ -1,5 +1,10 @@ +using System.Diagnostics; +using System.Threading.Tasks.Dataflow; + using EventSourcing.Backbone; +using FakeItEasy; + using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -8,6 +13,10 @@ using OpenTelemetry.Resources; using OpenTelemetry.Trace; +using StackExchange.Redis; + +using static ConsoleTest.Constants; + // see: // https://opentelemetry.io/docs/instrumentation/net/getting-started/ // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables @@ -20,13 +29,86 @@ namespace ConsoleTest; internal class Worker : BackgroundService { private readonly ILogger _logger; + private readonly IFooProducer _producer; + private readonly IConsumerLifetime _subscription; + private readonly CancellationTokenSource _cancellation; - public Worker(ILogger logger) + public Worker(ILogger logger, + IFooProducer producer, + IConsumerLifetime subscription, + CancellationTokenSource cancellation) { _logger = logger; + _producer = producer; + _subscription = subscription; + _cancellation = cancellation; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + _logger.LogInformation("Console Test"); + + Prepare(Subscriber); + + var sw = Stopwatch.StartNew(); + + var snapshot = sw.Elapsed; + _logger.LogInformation($"Build producer = {snapshot:mm\\:ss\\.ff}"); + snapshot = sw.Elapsed; + + var ab = new ActionBlock(async i => + { + await _producer.Event1Async(); + await _producer.Event2Async(i); + await _producer.Event3Async($"e-{1}"); + }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 }); + for (int i = 0; i < MAX; i++) + { + ab.Post(i); + //await _producer.Event1Async(); + //await _producer.Event2Async(i); + //await _producer.Event3Async($"e-{1}"); + } + ab.Complete(); + await ab.Completion; + + snapshot = sw.Elapsed - snapshot; + _logger.LogInformation($"Produce = {snapshot:mm\\:ss\\.ff}"); + snapshot = sw.Elapsed; + + await _subscription.Completion; + + snapshot = sw.Elapsed - snapshot; + _logger.LogInformation($"Consumed = {snapshot:mm\\:ss\\.ff}"); + + try + { + A.CallTo(() => Subscriber.Event1Async(A.Ignored)) + .MustHaveHappened(MAX, Times.Exactly); + A.CallTo(() => Subscriber.Event2Async(A.Ignored, A.Ignored)) + .MustHaveHappened(MAX, Times.Exactly); + A.CallTo(() => Subscriber.Event3Async(A.Ignored, A.Ignored)) + .MustHaveHappened(MAX, Times.Exactly); + } + catch (Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + _logger.LogInformation(ex.FormatLazy()); + Console.ResetColor(); + } + + _logger.LogInformation("Done"); + _cancellation.CancelSafe(); + } + + + static void Prepare(IFooConsumer subscriber) + { + A.CallTo(() => subscriber.Event1Async(A.Ignored)) + .ReturnsLazily(() => ValueTask.CompletedTask); + A.CallTo(() => subscriber.Event2Async(A.Ignored, A.Ignored)) + .ReturnsLazily(() => ValueTask.CompletedTask); + A.CallTo(() => subscriber.Event3Async(A.Ignored, A.Ignored)) + .ReturnsLazily(() => ValueTask.CompletedTask); } } From 24b4ddeb9befcdb0ce907794b81451e7718219d0 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 20 Jun 2023 09:29:24 +0300 Subject: [PATCH 169/178] feat: pass cancellation token --- .../RedisConsumerChannel.cs | 30 +++---- .../RedisHashStorageStrategy.cs | 2 +- .../RedisProducerChannel.cs | 2 +- .../RedisHashStorageStrategy.cs | 2 +- .../EventSourceRedisConnectionFactory.cs | 20 +++-- .../IEventSourceRedisConnectionFactory.cs | 12 ++- .../RedisCommonProviderExtensions.cs | 10 ++- ...EventSourcing.Backbone.Abstractions.csproj | 2 +- .../EventSourcing.Backbone.csproj | 3 - Tests/ConsoleTest/ConsoleTest.csproj | 2 - Tests/ConsoleTest/Constants.cs | 2 +- ...tSourcing.Backbone.IntegrationTests.csproj | 1 - .../EventSourcing.Backbone.UnitTests.csproj | 1 - .../Controllers/EventSourceApiController.cs | 5 +- .../Controllers/TestController.cs | 4 +- ...EventSourcing.Backbone.WebEventTest.csproj | 5 ++ .../OpenTelemetryExtensions.cs | 82 +++++++++++++++++++ .../Program.cs | 5 +- 18 files changed, 140 insertions(+), 50 deletions(-) create mode 100644 Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 4f293311..eed8766b 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -219,12 +219,14 @@ private async Task SubsribeToSingleAsync( await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, RedisChannelConstants.NONE_CONSUMER, - logger); + logger, + cancellationToken); await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, - logger); + logger, + cancellationToken); #endregion // await db.CreateConsumerGroupIfNotExistsAsync(...) @@ -462,7 +464,7 @@ async Task ReadBatchAsync() { isFirstBatchOrFailure = false; - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { @@ -484,7 +486,7 @@ async Task ReadBatchAsync() await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, - logger); + logger, cancellationToken); } catch (RedisServerException ex) { @@ -492,7 +494,7 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, - logger); + logger, cancellationToken); } #endregion // Exception Handling @@ -530,7 +532,7 @@ async Task ReadSelfPending() return values; - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { @@ -566,7 +568,7 @@ async Task ReadSelfPending() await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, - logger); + logger, cancellationToken); return Array.Empty(); } @@ -590,7 +592,7 @@ async Task ClaimStaleMessages( return values; try { - IDatabaseAsync db = await _connFactory.GetDatabaseAsync(); + IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); StreamPendingInfo pendingInfo = await db.StreamPendingAsync(key, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); foreach (var c in pendingInfo.Consumers) { @@ -675,7 +677,7 @@ async ValueTask AckAsync(RedisValue messageId) { try { - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); // release the event (won't handle again in the future) await db.StreamAcknowledgeAsync(key, @@ -725,7 +727,7 @@ ValueTask CancelAsync(IEnumerable messageIds) // Releases the messages (work around). async Task ReleaseAsync(RedisValue[] freeTargets) { - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { @@ -751,7 +753,7 @@ await db.StreamClaimAsync(plan.FullUri(), await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, plan.ConsumerGroup, - logger); + logger, cancellationToken); } #endregion // Exception Handling @@ -780,7 +782,7 @@ async ValueTask IConsumerChannelProvider.GetByIdAsync( try { - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); StreamEntry entry = await FindAsync(entryId); @@ -909,7 +911,7 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable ConsumerAsyncEnumerableOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken) { - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); var loop = AsyncLoop().WithCancellation(cancellationToken); await foreach (StreamEntry entry in loop) @@ -1071,7 +1073,7 @@ public async IAsyncEnumerable GetKeysUnsafeAsync( string pattern, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - IConnectionMultiplexer multiplexer = await _connFactory.GetAsync(); + IConnectionMultiplexer multiplexer = await _connFactory.GetAsync(cancellationToken); var distict = new HashSet(); while (!cancellationToken.IsCancellationRequested) { diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 789a3158..2ce61351 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -43,7 +43,7 @@ async ValueTask IConsumerStorageStrategy.LoadBucketAsync( { string key = $"{meta.FullUri()}:{type}:{meta.MessageId}"; - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellation); IDatabaseAsync db = conn.GetDatabase(); HashEntry[] entities; try diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 0087cbed..07ba6a97 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -101,7 +101,7 @@ async Task LocalStreamAddAsync() try { - IConnectionMultiplexer conn = await _connFactory.GetAsync(); + IConnectionMultiplexer conn = await _connFactory.GetAsync(CancellationToken.None); IDatabaseAsync db = conn.GetDatabase(); using var scope = SuppressInstrumentationScope.Begin(); var k = meta.FullUri(); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs index e034d6e5..c332d2cf 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -53,7 +53,7 @@ async ValueTask> IProducerStorageStrategy.S Metadata meta, CancellationToken cancellation) { - var conn = await _connFactory.GetAsync(); + var conn = await _connFactory.GetAsync(cancellation); try { IDatabaseAsync db = conn.GetDatabase(); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs index aadba8fa..235b4adc 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Logging; +using System.Threading; + +using Microsoft.Extensions.Logging; using StackExchange.Redis; @@ -146,9 +148,11 @@ public static IEventSourceRedisConnectionFactory CreateFromEnv( #region GetAsync /// - /// Get a valid connection + /// Get a valid connection /// - async Task IEventSourceRedisConnectionFactory.GetAsync() + /// The cancellation token. + /// + async Task IEventSourceRedisConnectionFactory.GetAsync(CancellationToken cancellationToken) { var conn = await _redisTask; if (conn.IsConnected) @@ -157,7 +161,7 @@ async Task IEventSourceRedisConnectionFactory.GetAsync() _logger.LogWarning("REDIS Connection [{kind}] [{ClientName}]: status = [{status}]", Kind, conn.ClientName, status); - var disp = await _lock.AcquireAsync(); + var disp = await _lock.AcquireAsync(cancellationToken); using (disp) { conn = await _redisTask; @@ -187,12 +191,14 @@ async Task IEventSourceRedisConnectionFactory.GetAsync() #region GetDatabaseAsync /// - /// Get database + /// Get database /// - async Task IEventSourceRedisConnectionFactory.GetDatabaseAsync() + /// The cancellation token. + /// + async Task IEventSourceRedisConnectionFactory.GetDatabaseAsync(CancellationToken cancellationToken) { IEventSourceRedisConnectionFactory self = this; - IConnectionMultiplexer conn = await self.GetAsync(); + IConnectionMultiplexer conn = await self.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); return db; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs index ecb64382..6d54f837 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/IEventSourceRedisConnectionFactory.cs @@ -8,12 +8,16 @@ namespace EventSourcing.Backbone public interface IEventSourceRedisConnectionFactory { /// - /// Get a valid connection + /// Get a valid connection /// - Task GetAsync(); + /// The cancellation token. + /// + Task GetAsync(CancellationToken cancellationToken); /// - /// Get database + /// Get database /// - Task GetDatabaseAsync(); + /// The cancellation token. + /// + Task GetDatabaseAsync(CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 297cafb0..9ce38e49 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -23,12 +23,14 @@ public static class RedisCommonProviderExtensions /// The event source key. /// The consumer group. /// The logger. + /// The cancellation token. /// public static async Task CreateConsumerGroupIfNotExistsAsync( this IEventSourceRedisConnectionFactory connFactory, string eventSourceKey, string consumerGroup, - ILogger logger) + ILogger logger, + CancellationToken cancellationToken) { StreamGroupInfo[] groupsInfo = Array.Empty(); @@ -39,7 +41,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { tryNumber++; - IConnectionMultiplexer conn = await connFactory.GetAsync(); + IConnectionMultiplexer conn = await connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { @@ -72,7 +74,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( #endregion // delay on retry - using var lk = await _lock.AcquireAsync(); + using var lk = await _lock.AcquireAsync(cancellationToken); groupsInfo = await db.StreamGroupInfoAsync( eventSourceKey, flags: CommandFlags.DemandMaster); @@ -111,7 +113,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { try { - using var lk = await _lock.AcquireAsync(); + using var lk = await _lock.AcquireAsync(cancellationToken); if (await db.StreamCreateConsumerGroupAsync(eventSourceKey, consumerGroup, StreamPosition.Beginning, diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index e4795dbc..8433008c 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -17,7 +17,7 @@ - + diff --git a/EventSourcing.Backbone/EventSourcing.Backbone.csproj b/EventSourcing.Backbone/EventSourcing.Backbone.csproj index fe568197..229e3ee3 100644 --- a/EventSourcing.Backbone/EventSourcing.Backbone.csproj +++ b/EventSourcing.Backbone/EventSourcing.Backbone.csproj @@ -19,9 +19,6 @@ - - - diff --git a/Tests/ConsoleTest/ConsoleTest.csproj b/Tests/ConsoleTest/ConsoleTest.csproj index 129639fe..fdead6be 100644 --- a/Tests/ConsoleTest/ConsoleTest.csproj +++ b/Tests/ConsoleTest/ConsoleTest.csproj @@ -20,10 +20,8 @@ - - diff --git a/Tests/ConsoleTest/Constants.cs b/Tests/ConsoleTest/Constants.cs index 830bf968..8fd2f8a9 100644 --- a/Tests/ConsoleTest/Constants.cs +++ b/Tests/ConsoleTest/Constants.cs @@ -4,7 +4,7 @@ namespace ConsoleTest; internal static class Constants { - public const int MAX = 1_000; + public const int MAX = 3_000; public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; public const string ENV = $"console-test"; public static readonly IFooConsumer Subscriber = A.Fake(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index a2312003..fa72a0a0 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -48,7 +48,6 @@ - diff --git a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj index a1c2e8bf..a99f1026 100644 --- a/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj +++ b/Tests/EventSourcing.Backbone.UnitTests/EventSourcing.Backbone.UnitTests.csproj @@ -21,7 +21,6 @@ - diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs index 274b5125..e0d58b3c 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs @@ -15,20 +15,17 @@ public class EventSourceApiController : ControllerBase private readonly IEventFlowProducer _eventFlowProducer; private readonly IConsumerReadyBuilder _consumerBuilder; private readonly IConsumerHooksBuilder _baseBuilder; - private readonly IDatabase _db; public EventSourceApiController( ILogger logger, IEventFlowProducer eventFlowProducer, IConsumerReadyBuilder consumerBuilder, - IConsumerHooksBuilder baseBuilder, - IConnectionMultiplexer redis) + IConsumerHooksBuilder baseBuilder) { _logger = logger; _eventFlowProducer = eventFlowProducer; _consumerBuilder = consumerBuilder; _baseBuilder = baseBuilder; - _db = redis.GetDatabase(); } #region GetAsync diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs index 4a5e04ed..0853ef50 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/TestController.cs @@ -49,7 +49,7 @@ public async ValueTask GetAsync() [ProducesResponseType(StatusCodes.Status201Created)] public async ValueTask GetPingAsync() { - IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(); + IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(CancellationToken.None); var p = await db.PingAsync(); _logger.LogInformation("Schema: {schema}", Request.Scheme); return p; @@ -65,7 +65,7 @@ public async ValueTask GetPingAsync() [ProducesResponseType(StatusCodes.Status201Created)] public async ValueTask GetStaticAsync() { - IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(); + IDatabaseAsync db = await _connFacroty.GetDatabaseAsync(CancellationToken.None); var p = await db.PingAsync(); return @$"ping: {p}"; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index 95c36f2d..22461154 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -24,6 +24,11 @@ + + + + + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs new file mode 100644 index 00000000..6d275050 --- /dev/null +++ b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs @@ -0,0 +1,82 @@ +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +// see: +// https://opentelemetry.io/docs/instrumentation/net/getting-started/ +// https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + +namespace EventSourcing.Backbone.WebEventTest; + +/// +/// Open telemetry extensions for ASP.NET Core +/// +internal static class OpenTelemetryExtensions +{ + #region AddOpenTelemetryEventSourcing + + /// + /// Adds open telemetry for event sourcing. + /// + /// The builder. + /// + public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicationBuilder builder) + { + IWebHostEnvironment environment = builder.Environment; + IServiceCollection services = builder.Services; + + // see: + // https://opentelemetry.io/docs/instrumentation/net/getting-started/ + // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables + services.AddOpenTelemetry() + .WithEventSourcingTracing(environment, + cfg => + { + cfg.AddAspNetCoreInstrumentation(m => + { + m.Filter = TraceFilter; + m.RecordException = true; + m.EnableGrpcAspNetCoreSupport = true; + }) + .AddHttpClientInstrumentation(m => + { + // m.Enrich + m.RecordException = true; + }) + .AddOtlpExporter(); + //if (environment.IsDevelopment()) + // cfg.AddConsoleExporter(); + }) + .WithEventSourcingMetrics(environment, cfg => + { + cfg.AddAspNetCoreInstrumentation( /* m => m.Filter = filter */) + .AddOtlpExporter() + .AddPrometheusExporter(); + //if (environment.IsDevelopment()) + // cfg.AddConsoleExporter(); + }); + + return services; + } + + #endregion // AddOpenTelemetryEventSourcing + + #region TraceFilter + + /// + /// Telemetries the filter. + /// + /// The CTX. + /// + private static bool TraceFilter(HttpContext ctx) => ctx.Request.Path.Value switch + { + "/health" => false, + "/readiness" => false, + "/metrics" => false, + string x when x.StartsWith("/swagger") => false, + string x when x.StartsWith("/_framework/") => false, + string x when x.StartsWith("/_vs/") => false, + _ => true + }; + + #endregion // TraceFilter +} diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index eb2fa717..2c624cac 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -72,9 +72,8 @@ }); //IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); -services.AddOpenTelemetry() - .WithEventSourcingTracing(environment) - .WithEventSourcingMetrics(environment); +builder.AddOpenTelemetryEventSourcing(); + //services.AddOpenTelemetry(environment, shortAppName, redisConnection); From 81a124977d803015b177d4e6dd4ecaf315b260cd Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Tue, 20 Jun 2023 13:44:50 +0300 Subject: [PATCH 170/178] rfc: const separation --- .../ConsumerChannelConstants.cs | 12 + .../RedisConsumerBuilder.cs | 403 ++-- .../RedisConsumerChannel.cs | 1733 ++++++++--------- .../ProducerChannelConstants.cs | 12 + .../RedisProducerChannel.cs | 4 +- .../EventSourceConstants.cs | 52 +- .../Extensions/EventSourcingExtensions.cs | 20 +- ...g.Backbone.OpenTelemetry.Extensions.csproj | 12 + .../EventSourcingOtel.cs | 27 +- .../AckPatternsTests.cs | 3 +- .../EndToEndTests.cs | 3 + .../AspCoreExtensions.cs | 85 - .../Extensions/OpenTelemetryExtensions.cs | 4 +- 13 files changed, 1157 insertions(+), 1213 deletions(-) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs new file mode 100644 index 00000000..fedf4698 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs @@ -0,0 +1,12 @@ +namespace EventSourcing.Backbone.Channels; + +/// +/// Constants +/// +internal static class ConsumerChannelConstants +{ + /// + /// The name of redis consumer channel source + /// + public const string REDIS_CHANNEL_SOURCE = "redis-consumer-channel"; +} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs index 12649be9..20572904 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerBuilder.cs @@ -7,229 +7,228 @@ using StackExchange.Redis; -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone; + +public static class RedisConsumerBuilder { - public static class RedisConsumerBuilder + /// + /// Create REDIS consumer builder. + /// + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder Create( + string endpoint, + string? password = null, + Action? configurationHook = null) { - /// - /// Create REDIS consumer builder. - /// - /// The raw endpoint (not an environment variable). - /// The password (not an environment variable). - /// The configuration hook. - /// - public static IConsumerStoreStrategyBuilder Create( - string endpoint, - string? password = null, - Action? configurationHook = null) - { - var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); - return configuration.CreateRedisConsumerBuilder(); - } - /// - /// Create REDIS consumer builder. - /// - /// The configuration hook. - /// - public static IConsumerStoreStrategyBuilder Create( - Action? configurationHook = null) - { - var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); - return configuration.CreateRedisConsumerBuilder(); - } + var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + return configuration.CreateRedisConsumerBuilder(); + } + /// + /// Create REDIS consumer builder. + /// + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder Create( + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); + return configuration.CreateRedisConsumerBuilder(); + } - /// - /// Create REDIS consumer builder. - /// - /// The redis configuration. - /// The setting. - /// - public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( - this ConfigurationOptions options, - RedisConsumerChannelSetting? setting = null) - { - var stg = setting ?? RedisConsumerChannelSetting.Default; - var builder = ConsumerBuilder.Empty; - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - logger, - options, - stg); - return channel; - } - } + /// + /// Create REDIS consumer builder. + /// + /// The redis configuration. + /// The setting. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this ConfigurationOptions options, + RedisConsumerChannelSetting? setting = null) + { + var stg = setting ?? RedisConsumerChannelSetting.Default; + var builder = ConsumerBuilder.Empty; + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; - /// - /// Create REDIS consumer builder. - /// - /// The setting. - /// The configuration hook. - /// - public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( - this RedisConsumerChannelSetting setting, - Action? configurationHook = null) + IConsumerChannelProvider LocalCreate(ILogger logger) { - var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); - return configuration.CreateRedisConsumerBuilder(setting); + var channel = new RedisConsumerChannel( + logger, + options, + stg); + return channel; } + } - /// - /// Create REDIS consumer builder. - /// - /// The setting. - /// The raw endpoint (not an environment variable). - /// The password (not an environment variable). - /// The configuration hook. - /// - public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( - this RedisConsumerChannelSetting setting, - string endpoint, - string? password = null, - Action? configurationHook = null) - { - var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); - return configuration.CreateRedisConsumerBuilder(setting); - } + /// + /// Create REDIS consumer builder. + /// + /// The setting. + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this RedisConsumerChannelSetting setting, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(configurationHook); + return configuration.CreateRedisConsumerBuilder(setting); + } + /// + /// Create REDIS consumer builder. + /// + /// The setting. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this RedisConsumerChannelSetting setting, + string endpoint, + string? password = null, + Action? configurationHook = null) + { + var configuration = RedisClientFactory.CreateConfigurationOptions(endpoint, password, configurationHook); + return configuration.CreateRedisConsumerBuilder(setting); + } - /// - /// Create REDIS consumer builder. - /// - /// The credentials keys. - /// The setting. - /// The configuration hook. - /// - public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( - this RedisCredentialsEnvKeys credentialsKeys, - RedisConsumerChannelSetting? setting = null, - Action? configurationHook = null) - { - var configuration = credentialsKeys.CreateConfigurationOptions(configurationHook); - return configuration.CreateRedisConsumerBuilder(setting); - } - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The setting. - /// The redis configuration. - /// - public static IConsumerStoreStrategyBuilder UseRedisChannel( - this IConsumerBuilder builder, - RedisConsumerChannelSetting? setting = null, - ConfigurationOptions? redisConfiguration = null) - { - var stg = setting ?? RedisConsumerChannelSetting.Default; - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - logger, - redisConfiguration, - stg); - return channel; - } - } + /// + /// Create REDIS consumer builder. + /// + /// The credentials keys. + /// The setting. + /// The configuration hook. + /// + public static IConsumerStoreStrategyBuilder CreateRedisConsumerBuilder( + this RedisCredentialsEnvKeys credentialsKeys, + RedisConsumerChannelSetting? setting = null, + Action? configurationHook = null) + { + var configuration = credentialsKeys.CreateConfigurationOptions(configurationHook); + return configuration.CreateRedisConsumerBuilder(setting); + } - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// Environment keys of the credentials - /// The setting. - /// - public static IConsumerStoreStrategyBuilder UseRedisChannel( - this IConsumerBuilder builder, - RedisCredentialsEnvKeys credentialsKeys, - RedisConsumerChannelSetting? setting = null) + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The setting. + /// The redis configuration. + /// + public static IConsumerStoreStrategyBuilder UseRedisChannel( + this IConsumerBuilder builder, + RedisConsumerChannelSetting? setting = null, + ConfigurationOptions? redisConfiguration = null) + { + var stg = setting ?? RedisConsumerChannelSetting.Default; + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) { - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - logger, - credentialsKeys, - setting); - return channel; - } + var channel = new RedisConsumerChannel( + logger, + redisConfiguration, + stg); + return channel; } + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// Environment keys of the credentials + /// The setting. + /// + public static IConsumerStoreStrategyBuilder UseRedisChannel( + this IConsumerBuilder builder, + RedisCredentialsEnvKeys credentialsKeys, + RedisConsumerChannelSetting? setting = null) + { + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The redis client factory. - /// The setting. - /// - internal static IConsumerStoreStrategyBuilder UseRedisChannel( - this IConsumerBuilder builder, - IEventSourceRedisConnectionFactory redisClientFactory, - RedisConsumerChannelSetting? setting = null) + IConsumerChannelProvider LocalCreate(ILogger logger) { - var channelBuilder = builder.UseChannel(LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var channel = new RedisConsumerChannel( - redisClientFactory, - logger, - setting); - return channel; - } + var channel = new RedisConsumerChannel( + logger, + credentialsKeys, + setting); + return channel; } + } + + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The redis client factory. + /// The setting. + /// + internal static IConsumerStoreStrategyBuilder UseRedisChannel( + this IConsumerBuilder builder, + IEventSourceRedisConnectionFactory redisClientFactory, + RedisConsumerChannelSetting? setting = null) + { + var channelBuilder = builder.UseChannel(LocalCreate); + return channelBuilder; - /// - /// Uses REDIS consumer channel. - /// - /// The builder. - /// The service provider. - /// The setting. - /// - /// redisClient - public static IConsumerIocStoreStrategyBuilder ResolveRedisConsumerChannel( - this IConsumerBuilder builder, - IServiceProvider serviceProvider, - RedisConsumerChannelSetting? setting = null) + IConsumerChannelProvider LocalCreate(ILogger logger) { - var channelBuilder = builder.UseChannel(serviceProvider, LocalCreate); - return channelBuilder; - - IConsumerChannelProvider LocalCreate(ILogger logger) - { - var connFactory = serviceProvider.GetService(); - if (connFactory == null) - throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFactory)} is not registered, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); - var channel = new RedisConsumerChannel( - connFactory, - logger, - setting); - return channel; - } + var channel = new RedisConsumerChannel( + redisClientFactory, + logger, + setting); + return channel; } + } - /// - /// Uses REDIS consumer channel. - /// - /// The service provider. - /// The setting. - /// - /// redisClient - public static IConsumerIocStoreStrategyBuilder ResolveRedisConsumerChannel( - this IServiceProvider serviceProvider, - RedisConsumerChannelSetting? setting = null) + /// + /// Uses REDIS consumer channel. + /// + /// The builder. + /// The service provider. + /// The setting. + /// + /// redisClient + public static IConsumerIocStoreStrategyBuilder ResolveRedisConsumerChannel( + this IConsumerBuilder builder, + IServiceProvider serviceProvider, + RedisConsumerChannelSetting? setting = null) + { + var channelBuilder = builder.UseChannel(serviceProvider, LocalCreate); + return channelBuilder; + + IConsumerChannelProvider LocalCreate(ILogger logger) { - var result = ConsumerBuilder.Empty.ResolveRedisConsumerChannel(serviceProvider, setting); - return result; + var connFactory = serviceProvider.GetService(); + if (connFactory == null) + throw new RedisConnectionException(ConnectionFailureType.None, $"{nameof(IEventSourceRedisConnectionFactory)} is not registered, use services.{nameof(RedisDiExtensions.AddEventSourceRedisConnection)} in order to register it at Setup stage."); + var channel = new RedisConsumerChannel( + connFactory, + logger, + setting); + return channel; } } + + /// + /// Uses REDIS consumer channel. + /// + /// The service provider. + /// The setting. + /// + /// redisClient + public static IConsumerIocStoreStrategyBuilder ResolveRedisConsumerChannel( + this IServiceProvider serviceProvider, + RedisConsumerChannelSetting? setting = null) + { + var result = ConsumerBuilder.Empty.ResolveRedisConsumerChannel(serviceProvider, setting); + return result; + } } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index eed8766b..c34d0fa0 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -16,1086 +16,1085 @@ // TODO: [bnaya 2021-07] MOVE TELEMETRY TO THE BASE CLASSES OF PRODUCER / CONSUME -namespace EventSourcing.Backbone.Channels.RedisProvider +namespace EventSourcing.Backbone.Channels.RedisProvider; + +/// +/// The redis consumer channel. +/// +internal class RedisConsumerChannel : IConsumerChannelProvider { + private const string BEGIN_OF_STREAM = "0000000000000"; + /// + /// The read by identifier chunk size. + /// REDIS don't have option to read direct position (it read from a position, not includes the position itself), + /// therefore read should start before the actual position. + /// + private const int READ_BY_ID_CHUNK_SIZE = 10; /// - /// The redis consumer channel. + /// Receiver max iterations /// - internal class RedisConsumerChannel : IConsumerChannelProvider + private const int READ_BY_ID_ITERATIONS = 1000 / READ_BY_ID_CHUNK_SIZE; + private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); + + private readonly ILogger _logger; + private readonly RedisConsumerChannelSetting _setting; + private readonly IEventSourceRedisConnectionFactory _connFactory; + private readonly IConsumerStorageStrategy _defaultStorageStrategy; + private const string META_SLOT = "____"; + private const int INIT_RELEASE_DELAY = 100; + private const int MAX_RELEASE_DELAY = 1000 * 30; // 30 seconds + + #region Ctor + + /// + /// Initializes a new instance. + /// + /// The redis provider promise. + /// The logger. + /// The setting. + public RedisConsumerChannel( + IEventSourceRedisConnectionFactory redisConnFactory, + ILogger logger, + RedisConsumerChannelSetting? setting = null) { - private const string BEGIN_OF_STREAM = "0000000000000"; - /// - /// The read by identifier chunk size. - /// REDIS don't have option to read direct position (it read from a position, not includes the position itself), - /// therefore read should start before the actual position. - /// - private const int READ_BY_ID_CHUNK_SIZE = 10; - /// - /// Receiver max iterations - /// - private const int READ_BY_ID_ITERATIONS = 1000 / READ_BY_ID_CHUNK_SIZE; - private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(EventSourceConstants.REDIS_CONSUMER_CHANNEL_SOURCE); - - private readonly ILogger _logger; - private readonly RedisConsumerChannelSetting _setting; - private readonly IEventSourceRedisConnectionFactory _connFactory; - private readonly IConsumerStorageStrategy _defaultStorageStrategy; - private const string META_SLOT = "____"; - private const int INIT_RELEASE_DELAY = 100; - private const int MAX_RELEASE_DELAY = 1000 * 30; // 30 seconds - - #region Ctor - - /// - /// Initializes a new instance. - /// - /// The redis provider promise. - /// The logger. - /// The setting. - public RedisConsumerChannel( - IEventSourceRedisConnectionFactory redisConnFactory, - ILogger logger, - RedisConsumerChannelSetting? setting = null) - { - _logger = logger; - _connFactory = redisConnFactory; - _defaultStorageStrategy = new RedisHashStorageStrategy(redisConnFactory); - _setting = setting ?? RedisConsumerChannelSetting.Default; - } + _logger = logger; + _connFactory = redisConnFactory; + _defaultStorageStrategy = new RedisHashStorageStrategy(redisConnFactory); + _setting = setting ?? RedisConsumerChannelSetting.Default; + } - /// - /// Initializes a new instance. - /// - /// The logger. - /// The configuration. - /// The setting. - public RedisConsumerChannel( - ILogger logger, - ConfigurationOptions? configuration = null, - RedisConsumerChannelSetting? setting = null) : this( - EventSourceRedisConnectionFactory.Create( - logger, - configuration), - logger, - setting) - { - } + /// + /// Initializes a new instance. + /// + /// The logger. + /// The configuration. + /// The setting. + public RedisConsumerChannel( + ILogger logger, + ConfigurationOptions? configuration = null, + RedisConsumerChannelSetting? setting = null) : this( + EventSourceRedisConnectionFactory.Create( + logger, + configuration), + logger, + setting) + { + } - /// - /// Initializes a new instance. - /// - /// The logger. - /// Environment keys of the credentials - /// The setting. - /// The configuration hook. - public RedisConsumerChannel( - ILogger logger, - IRedisCredentials credentialsKeys, - RedisConsumerChannelSetting? setting = null, - Action? configurationHook = null) : this( - EventSourceRedisConnectionFactory.Create( - credentialsKeys, - logger, - configurationHook), - logger, - setting) - { - } + /// + /// Initializes a new instance. + /// + /// The logger. + /// Environment keys of the credentials + /// The setting. + /// The configuration hook. + public RedisConsumerChannel( + ILogger logger, + IRedisCredentials credentialsKeys, + RedisConsumerChannelSetting? setting = null, + Action? configurationHook = null) : this( + EventSourceRedisConnectionFactory.Create( + credentialsKeys, + logger, + configurationHook), + logger, + setting) + { + } - /// - /// Initializes a new instance. - /// - /// The logger. - /// The raw endpoint (not an environment variable). - /// The password (not an environment variable). - /// The setting. - /// The configuration hook. - public RedisConsumerChannel( - ILogger logger, - string endpoint, - string? password = null, - RedisConsumerChannelSetting? setting = null, - Action? configurationHook = null) : this( - EventSourceRedisConnectionFactory.Create( - logger, - endpoint, - password, - configurationHook), - logger, - setting) - { - } + /// + /// Initializes a new instance. + /// + /// The logger. + /// The raw endpoint (not an environment variable). + /// The password (not an environment variable). + /// The setting. + /// The configuration hook. + public RedisConsumerChannel( + ILogger logger, + string endpoint, + string? password = null, + RedisConsumerChannelSetting? setting = null, + Action? configurationHook = null) : this( + EventSourceRedisConnectionFactory.Create( + logger, + endpoint, + password, + configurationHook), + logger, + setting) + { + } - #endregion // Ctor - - #region SubsribeAsync - - /// - /// Subscribe to the channel for specific metadata. - /// - /// The consumer plan. - /// The function. - /// The cancellation token. - /// - /// When completed - /// - public async ValueTask SubscribeAsync( - IConsumerPlan plan, - Func> func, - CancellationToken cancellationToken) - { - var joinCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(plan.Cancellation, cancellationToken); - var joinCancellation = joinCancellationSource.Token; - ConsumerOptions options = plan.Options; + #endregion // Ctor - ILogger? logger = _logger ?? plan.Logger; - logger.LogInformation("REDIS EVENT-SOURCE | SUBSCRIBE key: [{key}], consumer-group: [{consumer-group}], consumer-name: [{consumer-name}]", plan.FullUri(), plan.ConsumerGroup, plan.ConsumerName); + #region SubsribeAsync - while (!joinCancellation.IsCancellationRequested) - { - try - { - await SubsribeToSingleAsync(plan, func, options, joinCancellation); - // TODO: [bnaya 2023-05-22] think of the api for multi stream subscription (by partial uri * pattern) -> var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*").WithCancellation(cancellationToken) + /// + /// Subscribe to the channel for specific metadata. + /// + /// The consumer plan. + /// The function. + /// The cancellation token. + /// + /// When completed + /// + public async ValueTask SubscribeAsync( + IConsumerPlan plan, + Func> func, + CancellationToken cancellationToken) + { + var joinCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(plan.Cancellation, cancellationToken); + var joinCancellation = joinCancellationSource.Token; + ConsumerOptions options = plan.Options; - if (options.FetchUntilUnixDateOrEmpty != null) - break; - } - #region Exception Handling + ILogger? logger = _logger ?? plan.Logger; + logger.LogInformation("REDIS EVENT-SOURCE | SUBSCRIBE key: [{key}], consumer-group: [{consumer-group}], consumer-name: [{consumer-name}]", plan.FullUri(), plan.ConsumerGroup, plan.ConsumerName); - catch (OperationCanceledException) - { - if (_logger == null) - Console.WriteLine($"Subscribe cancellation [{plan.FullUri()}] event stream (may have reach the messages limit)"); - else - _logger.LogError("Subscribe cancellation [{uri}] event stream (may have reach the messages limit)", - plan.Uri); - joinCancellationSource.CancelSafe(); - } - catch (Exception ex) - { - if (_logger == null) - Console.WriteLine($"Fail to subscribe into the [{plan.FullUri()}] event stream"); - else - _logger.LogError(ex, "Fail to subscribe into the [{uri}] event stream", - plan.Uri); - throw; - } + while (!joinCancellation.IsCancellationRequested) + { + try + { + await SubsribeToSingleAsync(plan, func, options, joinCancellation); + // TODO: [bnaya 2023-05-22] think of the api for multi stream subscription (by partial uri * pattern) -> var keys = GetKeysUnsafeAsync(pattern: $"{partition}:*").WithCancellation(cancellationToken) + + if (options.FetchUntilUnixDateOrEmpty != null) + break; + } + #region Exception Handling - #endregion // Exception Handling + catch (OperationCanceledException) + { + if (_logger == null) + Console.WriteLine($"Subscribe cancellation [{plan.FullUri()}] event stream (may have reach the messages limit)"); + else + _logger.LogError("Subscribe cancellation [{uri}] event stream (may have reach the messages limit)", + plan.Uri); + joinCancellationSource.CancelSafe(); } + catch (Exception ex) + { + if (_logger == null) + Console.WriteLine($"Fail to subscribe into the [{plan.FullUri()}] event stream"); + else + _logger.LogError(ex, "Fail to subscribe into the [{uri}] event stream", + plan.Uri); + throw; + } + + #endregion // Exception Handling } + } - #endregion // SubsribeAsync - - #region SubsribeToSingleAsync - - /// - /// Subscribe to specific shard. - /// - /// The consumer plan. - /// The function. - /// The options. - /// The cancellation token. - private async Task SubsribeToSingleAsync( - IConsumerPlan plan, - Func> func, - ConsumerOptions options, - CancellationToken cancellationToken) - { - var claimingTrigger = options.ClaimingTrigger; - var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; + #endregion // SubsribeAsync - string key = plan.FullUri(); - bool isFirstBatchOrFailure = true; + #region SubsribeToSingleAsync - CommandFlags flags = CommandFlags.None; - string? fetchUntil = options.FetchUntilUnixDateOrEmpty?.ToString(); + /// + /// Subscribe to specific shard. + /// + /// The consumer plan. + /// The function. + /// The options. + /// The cancellation token. + private async Task SubsribeToSingleAsync( + IConsumerPlan plan, + Func> func, + ConsumerOptions options, + CancellationToken cancellationToken) + { + var claimingTrigger = options.ClaimingTrigger; + var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - ILogger logger = plan.Logger ?? _logger; + string key = plan.FullUri(); + bool isFirstBatchOrFailure = true; - #region await db.CreateConsumerGroupIfNotExistsAsync(...) + CommandFlags flags = CommandFlags.None; + string? fetchUntil = options.FetchUntilUnixDateOrEmpty?.ToString(); - await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, - RedisChannelConstants.NONE_CONSUMER, - logger, - cancellationToken); + ILogger logger = plan.Logger ?? _logger; - await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, - plan.ConsumerGroup, - logger, - cancellationToken); + #region await db.CreateConsumerGroupIfNotExistsAsync(...) - #endregion // await db.CreateConsumerGroupIfNotExistsAsync(...) + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + RedisChannelConstants.NONE_CONSUMER, + logger, + cancellationToken); - int releaseDelay = INIT_RELEASE_DELAY; - int bachSize = options.BatchSize; + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger, + cancellationToken); - TimeSpan delay = TimeSpan.Zero; - int emptyBatchCount = 0; - while (!cancellationToken.IsCancellationRequested) - { - var proceed = await HandleBatchAsync(); - if (!proceed) - break; - } + #endregion // await db.CreateConsumerGroupIfNotExistsAsync(...) - #region HandleBatchAsync + int releaseDelay = INIT_RELEASE_DELAY; + int bachSize = options.BatchSize; - // Handle single batch - async ValueTask HandleBatchAsync() - { - var policy = _setting.Policy.Policy; - return await policy.ExecuteAsync(HandleBatchBreakerAsync, cancellationToken); - } + TimeSpan delay = TimeSpan.Zero; + int emptyBatchCount = 0; + while (!cancellationToken.IsCancellationRequested) + { + var proceed = await HandleBatchAsync(); + if (!proceed) + break; + } - #endregion // HandleBatchAsync + #region HandleBatchAsync + // Handle single batch + async ValueTask HandleBatchAsync() + { + var policy = _setting.Policy.Policy; + return await policy.ExecuteAsync(HandleBatchBreakerAsync, cancellationToken); + } - #region HandleBatchBreakerAsync + #endregion // HandleBatchAsync - async Task HandleBatchBreakerAsync(CancellationToken ct) - { - ct.ThrowIfCancellationRequested(); - StreamEntry[] results = await ReadBatchAsync(); - emptyBatchCount = results.Length == 0 ? emptyBatchCount + 1 : 0; - results = await ClaimStaleMessages(emptyBatchCount, results, ct); + #region HandleBatchBreakerAsync - if (results.Length == 0) - { - if (fetchUntil == null) - delay = await DelayIfEmpty(delay, cancellationToken); - return fetchUntil == null; - } + async Task HandleBatchBreakerAsync(CancellationToken ct) + { + ct.ThrowIfCancellationRequested(); - ct.ThrowIfCancellationRequested(); - - try - { - var batchCancellation = new CancellationTokenSource(); - int i = 0; - batchCancellation.Token.Register(async () => - { - // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured - RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); - await ReleaseAsync(freeTargets); - }); - // TODO: [bnaya 2023-06-19] enable parallel consuming (when order doesn't matters) See #RELEASE - for (; i < results.Length && !batchCancellation.IsCancellationRequested; i++) - { - StreamEntry result = results[i]; + StreamEntry[] results = await ReadBatchAsync(); + emptyBatchCount = results.Length == 0 ? emptyBatchCount + 1 : 0; + results = await ClaimStaleMessages(emptyBatchCount, results, ct); - #region Metadata meta = ... + if (results.Length == 0) + { + if (fetchUntil == null) + delay = await DelayIfEmpty(delay, cancellationToken); + return fetchUntil == null; + } - Dictionary channelMeta = result.Values.ToDictionary(m => m.Name, m => m.Value); - Metadata meta; - string? metaJson = channelMeta[META_SLOT]; - string eventKey = ((string?)result.Id) ?? throw new ArgumentException(nameof(MetadataExtensions.Empty.EventKey)); - if (string.IsNullOrEmpty(metaJson)) - { // backward comparability + ct.ThrowIfCancellationRequested(); + + try + { + var batchCancellation = new CancellationTokenSource(); + int i = 0; + batchCancellation.Token.Register(async () => + { + // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured + RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); + await ReleaseAsync(freeTargets); + }); + // TODO: [bnaya 2023-06-19] enable parallel consuming (when order doesn't matters) See #RELEASE + for (; i < results.Length && !batchCancellation.IsCancellationRequested; i++) + { + StreamEntry result = results[i]; - string channelType = ((string?)channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.ChannelType)); + #region Metadata meta = ... - if (channelType != CHANNEL_TYPE) - { - // TODO: [bnaya 2021-07] send metrics - logger.LogWarning($"{nameof(RedisConsumerChannel)} [{CHANNEL_TYPE}] omit handling message of type '{channelType}'"); - await AckAsync(result.Id); - continue; - } + Dictionary channelMeta = result.Values.ToDictionary(m => m.Name, m => m.Value); + Metadata meta; + string? metaJson = channelMeta[META_SLOT]; + string eventKey = ((string?)result.Id) ?? throw new ArgumentException(nameof(MetadataExtensions.Empty.EventKey)); + if (string.IsNullOrEmpty(metaJson)) + { // backward comparability - string id = ((string?)channelMeta[nameof(MetadataExtensions.Empty.MessageId)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.MessageId)); - string operation = ((string?)channelMeta[nameof(MetadataExtensions.Empty.Operation)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.Operation)); - long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; - DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); - if (fetchUntil != null && string.Compare(fetchUntil, result.Id) < 0) - return false; - meta = new Metadata - { - MessageId = id, - EventKey = eventKey, - Environment = plan.Environment, - Uri = plan.Uri, - Operation = operation, - ProducedAt = producedAt - }; + string channelType = ((string?)channelMeta[nameof(MetadataExtensions.Empty.ChannelType)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.ChannelType)); + if (channelType != CHANNEL_TYPE) + { + // TODO: [bnaya 2021-07] send metrics + logger.LogWarning($"{nameof(RedisConsumerChannel)} [{CHANNEL_TYPE}] omit handling message of type '{channelType}'"); + await AckAsync(result.Id); + continue; } - else + + string id = ((string?)channelMeta[nameof(MetadataExtensions.Empty.MessageId)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.MessageId)); + string operation = ((string?)channelMeta[nameof(MetadataExtensions.Empty.Operation)]) ?? throw new EventSourcingException(nameof(MetadataExtensions.Empty.Operation)); + long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; + DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); + if (fetchUntil != null && string.Compare(fetchUntil, result.Id) < 0) + return false; + meta = new Metadata { - meta = JsonSerializer.Deserialize(metaJson, EventSourceOptions.FullSerializerOptions) ?? throw new EventSourcingException(nameof(Metadata)); - meta = meta with { EventKey = eventKey }; + MessageId = id, + EventKey = eventKey, + Environment = plan.Environment, + Uri = plan.Uri, + Operation = operation, + ProducedAt = producedAt + }; - } + } + else + { + meta = JsonSerializer.Deserialize(metaJson, EventSourceOptions.FullSerializerOptions) ?? throw new EventSourcingException(nameof(Metadata)); + meta = meta with { EventKey = eventKey }; - #endregion // Metadata meta = ... + } - int local = i; - var cancellableIds = results[local..].Select(m => m.Id); - var ack = new AckOnce( - () => AckAsync(result.Id), - plan.Options.AckBehavior, logger, - async () => - { - batchCancellation.CancelSafe(); // cancel forward - await CancelAsync(cancellableIds); - }); + #endregion // Metadata meta = ... - #region OriginFilter + int local = i; + var cancellableIds = results[local..].Select(m => m.Id); + var ack = new AckOnce( + () => AckAsync(result.Id), + plan.Options.AckBehavior, logger, + async () => + { + batchCancellation.CancelSafe(); // cancel forward + await CancelAsync(cancellableIds); + }); - MessageOrigin originFilter = plan.Options.OriginFilter; - if (originFilter != MessageOrigin.None && (originFilter & meta.Origin) == MessageOrigin.None) - { - Ack.Set(ack); - #region Log + #region OriginFilter + + MessageOrigin originFilter = plan.Options.OriginFilter; + if (originFilter != MessageOrigin.None && (originFilter & meta.Origin) == MessageOrigin.None) + { + Ack.Set(ack); + #region Log - _logger.LogInformation("Event Source skip consuming of event [{event-key}] because if origin is [{origin}] while the origin filter is sets to [{origin-filter}], Operation:[{operation}], Stream:[{stream}]", meta.EventKey, meta.Origin, originFilter, meta.Operation, meta.FullUri()); + _logger.LogInformation("Event Source skip consuming of event [{event-key}] because if origin is [{origin}] while the origin filter is sets to [{origin-filter}], Operation:[{operation}], Stream:[{stream}]", meta.EventKey, meta.Origin, originFilter, meta.Operation, meta.FullUri()); - #endregion // Log - continue; - } + #endregion // Log + continue; + } - #endregion // OriginFilter + #endregion // OriginFilter - #region var announcement = new Announcement(...) + #region var announcement = new Announcement(...) - Bucket segmets = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Segments); - Bucket interceptions = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Interceptions); + Bucket segmets = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Segments); + Bucket interceptions = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Interceptions); - var announcement = new Announcement - { - Metadata = meta, - Segments = segmets, - InterceptorsData = interceptions - }; + var announcement = new Announcement + { + Metadata = meta, + Segments = segmets, + InterceptorsData = interceptions + }; - #endregion // var announcement = new Announcement(...) + #endregion // var announcement = new Announcement(...) - #region Start Telemetry Span + #region Start Telemetry Span - ActivityContext parentContext = meta.ExtractSpan(channelMeta, ExtractTraceContext); - // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - var activityName = $"{meta.Operation} consume"; + ActivityContext parentContext = meta.ExtractSpan(channelMeta, ExtractTraceContext); + // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + var activityName = $"{meta.Operation} consume"; - bool traceAsParent = (DateTimeOffset.UtcNow - meta.ProducedAt) < plan.Options.TraceAsParent; - ActivityContext parentActivityContext = traceAsParent ? parentContext : default; - using var activity = ACTIVITY_SOURCE.StartActivity( - activityName, - ActivityKind.Consumer, - parentActivityContext, links: new[] { new ActivityLink(parentContext) }); - meta.InjectTelemetryTags(activity); + bool traceAsParent = (DateTimeOffset.UtcNow - meta.ProducedAt) < plan.Options.TraceAsParent; + ActivityContext parentActivityContext = traceAsParent ? parentContext : default; + using var activity = ACTIVITY_SOURCE.StartActivity( + activityName, + ActivityKind.Consumer, + parentActivityContext, links: new[] { new ActivityLink(parentContext) }); + meta.InjectTelemetryTags(activity); - #region IEnumerable ExtractTraceContext(Dictionary entries, string key) + #region IEnumerable ExtractTraceContext(Dictionary entries, string key) - IEnumerable ExtractTraceContext(Dictionary entries, string key) + IEnumerable ExtractTraceContext(Dictionary entries, string key) + { + try { - try + if (entries.TryGetValue(key, out var value)) { - if (entries.TryGetValue(key, out var value)) - { - if (string.IsNullOrEmpty(value)) - return Array.Empty(); - return new[] { value.ToString() }; - } + if (string.IsNullOrEmpty(value)) + return Array.Empty(); + return new[] { value.ToString() }; } - #region Exception Handling + } + #region Exception Handling - catch (Exception ex) - { - Exception err = ex.FormatLazy(); - _logger.LogError(err, "Failed to extract trace context: {error}", err); - } + catch (Exception ex) + { + Exception err = ex.FormatLazy(); + _logger.LogError(err, "Failed to extract trace context: {error}", err); + } - #endregion // Exception Handling + #endregion // Exception Handling - return Enumerable.Empty(); - } + return Enumerable.Empty(); + } - #endregion // IEnumerable ExtractTraceContext(Dictionary entries, string key) + #endregion // IEnumerable ExtractTraceContext(Dictionary entries, string key) - #endregion // Start Telemetry Span + #endregion // Start Telemetry Span - bool succeed = await func(announcement, ack); - if (succeed) - { - releaseDelay = INIT_RELEASE_DELAY; - bachSize = options.BatchSize; - } - else + bool succeed = await func(announcement, ack); + if (succeed) + { + releaseDelay = INIT_RELEASE_DELAY; + bachSize = options.BatchSize; + } + else + { + // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured + if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) { - // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured - if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) - { - RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); - await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet - await Task.Delay(releaseDelay, ct); - } + RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); + await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet + await Task.Delay(releaseDelay, ct); } } } - catch - { - isFirstBatchOrFailure = true; - } - return true; } + catch + { + isFirstBatchOrFailure = true; + } + return true; + } - #endregion // HandleBatchBreakerAsync + #endregion // HandleBatchBreakerAsync - #region ReadBatchAsync + #region ReadBatchAsync - // read batch entities from REDIS - async Task ReadBatchAsync() + // read batch entities from REDIS + async Task ReadBatchAsync() + { + // TBD: circuit-breaker + try { - // TBD: circuit-breaker - try + var r = await _setting.Policy.Policy.ExecuteAsync(async (ct) => { - var r = await _setting.Policy.Policy.ExecuteAsync(async (ct) => + ct.ThrowIfCancellationRequested(); + StreamEntry[] values = Array.Empty(); + values = await ReadSelfPending(); + + if (values.Length == 0) { - ct.ThrowIfCancellationRequested(); - StreamEntry[] values = Array.Empty(); - values = await ReadSelfPending(); + isFirstBatchOrFailure = false; - if (values.Length == 0) + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); + IDatabaseAsync db = conn.GetDatabase(); + try { - isFirstBatchOrFailure = false; - - IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); - IDatabaseAsync db = conn.GetDatabase(); - try - { - values = await db.StreamReadGroupAsync( - key, - plan.ConsumerGroup, - plan.ConsumerName, - position: StreamPosition.NewMessages, - count: bachSize, - flags: flags) - .WithCancellation(ct, () => Array.Empty()) - .WithCancellation(cancellationToken, () => Array.Empty()); - } - #region Exception Handling - - catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) - { - logger.LogWarning(ex, ex.Message); - await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, - plan.ConsumerGroup, - logger, cancellationToken); - } - catch (RedisServerException ex) - { - logger.LogWarning(ex, ex.Message); - await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, - plan.ConsumerGroup, - logger, cancellationToken); - } + values = await db.StreamReadGroupAsync( + key, + plan.ConsumerGroup, + plan.ConsumerName, + position: StreamPosition.NewMessages, + count: bachSize, + flags: flags) + .WithCancellation(ct, () => Array.Empty()) + .WithCancellation(cancellationToken, () => Array.Empty()); + } + #region Exception Handling - #endregion // Exception Handling + catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) + { + logger.LogWarning(ex, ex.Message); + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger, cancellationToken); + } + catch (RedisServerException ex) + { + logger.LogWarning(ex, ex.Message); + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger, cancellationToken); } - StreamEntry[] results = values ?? Array.Empty(); - return results; - }, cancellationToken); - return r; - } - #region Exception Handling - catch (RedisTimeoutException ex) - { - logger.LogWarning(ex, "Event source [{source}] by [{consumer}]: Timeout", key, plan.ConsumerName); - return Array.Empty(); - } - catch (Exception ex) - { - logger.LogError(ex, "Fail to read from event source [{source}] by [{consumer}]", key, plan.ConsumerName); - return Array.Empty(); - } + #endregion // Exception Handling + } + StreamEntry[] results = values ?? Array.Empty(); + return results; + }, cancellationToken); + return r; + } + #region Exception Handling - #endregion // Exception Handling + catch (RedisTimeoutException ex) + { + logger.LogWarning(ex, "Event source [{source}] by [{consumer}]: Timeout", key, plan.ConsumerName); + return Array.Empty(); } + catch (Exception ex) + { + logger.LogError(ex, "Fail to read from event source [{source}] by [{consumer}]", key, plan.ConsumerName); + return Array.Empty(); + } + + #endregion // Exception Handling + } - #endregion // ReadBatchAsync + #endregion // ReadBatchAsync - #region ReadSelfPending + #region ReadSelfPending - // Check for pending messages of the current consumer (crash scenario) - async Task ReadSelfPending() - { - StreamEntry[] values = Array.Empty(); - if (!isFirstBatchOrFailure) - return values; + // Check for pending messages of the current consumer (crash scenario) + async Task ReadSelfPending() + { + StreamEntry[] values = Array.Empty(); + if (!isFirstBatchOrFailure) + return values; - IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); - IDatabaseAsync db = conn.GetDatabase(); - try + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); + IDatabaseAsync db = conn.GetDatabase(); + try + { + StreamPendingMessageInfo[] pendMsgInfo = await db.StreamPendingMessagesAsync( + key, + plan.ConsumerGroup, + options.BatchSize, + plan.ConsumerName, + flags: CommandFlags.DemandMaster); + if (pendMsgInfo != null && pendMsgInfo.Length != 0) { - StreamPendingMessageInfo[] pendMsgInfo = await db.StreamPendingMessagesAsync( - key, - plan.ConsumerGroup, - options.BatchSize, - plan.ConsumerName, - flags: CommandFlags.DemandMaster); - if (pendMsgInfo != null && pendMsgInfo.Length != 0) + var ids = pendMsgInfo + .Select(m => m.MessageId).ToArray(); + if (ids.Length != 0) { - var ids = pendMsgInfo - .Select(m => m.MessageId).ToArray(); - if (ids.Length != 0) - { - values = await db.StreamClaimAsync(key, - plan.ConsumerGroup, - plan.ConsumerName, - 0, - ids, - flags: CommandFlags.DemandMaster); - values = values ?? Array.Empty(); - _logger.LogInformation("Claimed messages: {ids}", ids); - } + values = await db.StreamClaimAsync(key, + plan.ConsumerGroup, + plan.ConsumerName, + 0, + ids, + flags: CommandFlags.DemandMaster); + values = values ?? Array.Empty(); + _logger.LogInformation("Claimed messages: {ids}", ids); } - - return values; } - #region Exception Handling - catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) - { - await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, - plan.ConsumerGroup, - logger, cancellationToken); - return Array.Empty(); - } + return values; + } + #region Exception Handling - #endregion // Exception Handling + catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) + { + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger, cancellationToken); + return Array.Empty(); } - #endregion // ReadSelfPending + #endregion // Exception Handling + } + + #endregion // ReadSelfPending - #region ClaimStaleMessages + #region ClaimStaleMessages - // Taking work from other consumers which have log-time's pending messages - async Task ClaimStaleMessages( - int emptyBatchCount, - StreamEntry[] values, - CancellationToken ct) + // Taking work from other consumers which have log-time's pending messages + async Task ClaimStaleMessages( + int emptyBatchCount, + StreamEntry[] values, + CancellationToken ct) + { + var logger = plan.Logger ?? _logger; + ct.ThrowIfCancellationRequested(); + if (values.Length != 0) return values; + if (emptyBatchCount < claimingTrigger.EmptyBatchCount) + return values; + try { - var logger = plan.Logger ?? _logger; - ct.ThrowIfCancellationRequested(); - if (values.Length != 0) return values; - if (emptyBatchCount < claimingTrigger.EmptyBatchCount) - return values; - try + IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); + StreamPendingInfo pendingInfo = await db.StreamPendingAsync(key, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); + foreach (var c in pendingInfo.Consumers) { - IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); - StreamPendingInfo pendingInfo = await db.StreamPendingAsync(key, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); - foreach (var c in pendingInfo.Consumers) + var self = c.Name == plan.ConsumerName; + if (self) continue; + try { - var self = c.Name == plan.ConsumerName; - if (self) continue; - try - { - var pendMsgInfo = await db.StreamPendingMessagesAsync( - key, - plan.ConsumerGroup, - 10, - c.Name, - pendingInfo.LowestPendingMessageId, - pendingInfo.HighestPendingMessageId, - flags: CommandFlags.DemandMaster); - - + var pendMsgInfo = await db.StreamPendingMessagesAsync( + key, + plan.ConsumerGroup, + 10, + c.Name, + pendingInfo.LowestPendingMessageId, + pendingInfo.HighestPendingMessageId, + flags: CommandFlags.DemandMaster); - RedisValue[] ids = pendMsgInfo - .Where(x => x.IdleTimeInMilliseconds > minIdleTime) - .Select(m => m.MessageId).ToArray(); - if (ids.Length == 0) - continue; - #region Log - logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} messages, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); - #endregion // Log - - // will claim messages only if older than _setting.ClaimingTrigger.MinIdleTime - values = await db.StreamClaimAsync(key, - plan.ConsumerGroup, - c.Name, - minIdleTime, - ids, - flags: CommandFlags.DemandMaster); - if (values.Length != 0) - logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} messages, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); - } - #region Exception Handling - - catch (RedisTimeoutException ex) - { - logger.LogWarning(ex, "Timeout (handle pending): {name}{self}", c.Name, self); + RedisValue[] ids = pendMsgInfo + .Where(x => x.IdleTimeInMilliseconds > minIdleTime) + .Select(m => m.MessageId).ToArray(); + if (ids.Length == 0) continue; - } - catch (Exception ex) - { - logger.LogError(ex, "Fail to claim pending: {name}{self}", c.Name, self); - } + #region Log + logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} messages, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); - #endregion // Exception Handling + #endregion // Log - if (values != null && values.Length != 0) - return values; + // will claim messages only if older than _setting.ClaimingTrigger.MinIdleTime + values = await db.StreamClaimAsync(key, + plan.ConsumerGroup, + c.Name, + minIdleTime, + ids, + flags: CommandFlags.DemandMaster); + if (values.Length != 0) + logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} messages, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); } - } - #region Exception Handling + #region Exception Handling - catch (RedisConnectionException ex) - { - _logger.LogWarning(ex, "Fail to claim REDIS's pending"); - } + catch (RedisTimeoutException ex) + { + logger.LogWarning(ex, "Timeout (handle pending): {name}{self}", c.Name, self); + continue; + } - catch (Exception ex) - { - _logger.LogError(ex, "Fail to claim pending"); - } + catch (Exception ex) + { + logger.LogError(ex, "Fail to claim pending: {name}{self}", c.Name, self); + } - #endregion // Exception Handling + #endregion // Exception Handling - return Array.Empty(); + if (values != null && values.Length != 0) + return values; + } } + #region Exception Handling - #endregion // ClaimStaleMessages - - #region AckAsync - - // Acknowledge event handling (prevent re-consuming of the message). - async ValueTask AckAsync(RedisValue messageId) + catch (RedisConnectionException ex) { - try - { - IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); - IDatabaseAsync db = conn.GetDatabase(); - // release the event (won't handle again in the future) - await db.StreamAcknowledgeAsync(key, - plan.ConsumerGroup, - messageId, - flags: CommandFlags.DemandMaster); - } - catch (Exception ex) - { - // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class - logger.LogWarning(ex.FormatLazy(), $"Fail to acknowledge message [{messageId}]"); - throw; - } + _logger.LogWarning(ex, "Fail to claim REDIS's pending"); } - #endregion // AckAsync - - #region CancelAsync - - // Cancels the asynchronous. - ValueTask CancelAsync(IEnumerable messageIds) + catch (Exception ex) { - // no way to release consumed item back to the stream - //try - //{ - // // release the event (won't handle again in the future) - // await db.StreamClaimIdsOnlyAsync(key, - // plan.ConsumerGroup, - // RedisValue.Null, - // 0, - // messageIds.ToArray(), - // flags: CommandFlags.DemandMaster); - //} - //catch (Exception) - //{ // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class - // throw; - //} - return ValueTask.CompletedTask; - + _logger.LogError(ex, "Fail to claim pending"); } - #endregion // CancelAsync - - #region ReleaseAsync - - - // Releases the messages (work around). - async Task ReleaseAsync(RedisValue[] freeTargets) - { - IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); - IDatabaseAsync db = conn.GetDatabase(); - try - { - await db.StreamClaimAsync(plan.FullUri(), - plan.ConsumerGroup, - RedisChannelConstants.NONE_CONSUMER, - 1, - freeTargets, - flags: CommandFlags.DemandMaster); - await Task.Delay(releaseDelay, cancellationToken); - if (releaseDelay < MAX_RELEASE_DELAY) - releaseDelay = Math.Min(releaseDelay * 2, MAX_RELEASE_DELAY); - - if (bachSize == options.BatchSize) - bachSize = 1; - else - bachSize = Math.Min(bachSize * 2, options.BatchSize); - } - #region Exception Handling + #endregion // Exception Handling - catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) - { - await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, - plan.ConsumerGroup, - logger, cancellationToken); - } + return Array.Empty(); + } - #endregion // Exception Handling - } + #endregion // ClaimStaleMessages - #endregion // ReleaseAsync - } + #region AckAsync - #endregion // SubsribeToSingleAsync - - #region GetByIdAsync - - /// - /// Gets announcement data by id. - /// - /// The entry identifier. - /// The plan. - /// The cancellation token. - /// - async ValueTask IConsumerChannelProvider.GetByIdAsync( - EventKey entryId, - IConsumerPlan plan, - CancellationToken cancellationToken) + // Acknowledge event handling (prevent re-consuming of the message). + async ValueTask AckAsync(RedisValue messageId) { - string mtdName = $"{nameof(IConsumerChannelProvider)}.{nameof(IConsumerChannelProvider.GetByIdAsync)}"; - try { IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); - StreamEntry entry = await FindAsync(entryId); - - #region var announcement = new Announcement(...) - - Dictionary channelMeta = entry.Values.ToDictionary(m => m.Name, m => m.Value); - string channelType = GetMeta(nameof(MetadataExtensions.Empty.ChannelType)); - string id = GetMeta(nameof(MetadataExtensions.Empty.MessageId)); - string operation = GetMeta(nameof(MetadataExtensions.Empty.Operation)); - long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; - - #region string GetMeta(string propKey) - - string GetMeta(string propKey) - { - string? result = channelMeta[propKey]; - if (result == null) throw new ArgumentNullException(propKey); - return result; - } - - #endregion // string GetMeta(string propKey) + // release the event (won't handle again in the future) + await db.StreamAcknowledgeAsync(key, + plan.ConsumerGroup, + messageId, + flags: CommandFlags.DemandMaster); + } + catch (Exception ex) + { + // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class + logger.LogWarning(ex.FormatLazy(), $"Fail to acknowledge message [{messageId}]"); + throw; + } + } - DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); -#pragma warning disable CS8601 // Possible null reference assignment. - var meta = new Metadata - { - MessageId = id, - EventKey = entry.Id, - Environment = plan.Environment, - Uri = plan.Uri, - Operation = operation, - ProducedAt = producedAt, - ChannelType = channelType - }; -#pragma warning restore CS8601 // Possible null reference assignment. + #endregion // AckAsync - Bucket segmets = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Segments); - Bucket interceptions = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Interceptions); + #region CancelAsync - var announcement = new Announcement - { - Metadata = meta, - Segments = segmets, - InterceptorsData = interceptions - }; + // Cancels the asynchronous. + ValueTask CancelAsync(IEnumerable messageIds) + { + // no way to release consumed item back to the stream + //try + //{ + // // release the event (won't handle again in the future) + // await db.StreamClaimIdsOnlyAsync(key, + // plan.ConsumerGroup, + // RedisValue.Null, + // 0, + // messageIds.ToArray(), + // flags: CommandFlags.DemandMaster); + //} + //catch (Exception) + //{ // TODO: [bnaya 2020-10] do better handling (re-throw / swallow + reason) currently logged at the wrapping class + // throw; + //} + return ValueTask.CompletedTask; - #endregion // var announcement = new Announcement(...) + } - return announcement; + #endregion // CancelAsync - #region FindAsync + #region ReleaseAsync - async Task FindAsync(EventKey entryId) - { - string lookForId = (string)entryId; - string key = plan.FullUri(); - - string originId = lookForId; - int len = originId.IndexOf('-'); - string fromPrefix = originId.Substring(0, len); - long start = long.Parse(fromPrefix); - string startPosition = (start - 1).ToString(); - int iteration = 0; - for (int i = 0; i < READ_BY_ID_ITERATIONS; i++) // up to 1000 items - { - iteration++; - StreamEntry[] entries = await db.StreamReadAsync( - key, - startPosition, - READ_BY_ID_CHUNK_SIZE, - CommandFlags.DemandMaster); - if (entries.Length == 0) - throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return nothing, start at ({startPosition}, iteration = {iteration})."); - string k = string.Empty; - foreach (StreamEntry e in entries) - { -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. -#pragma warning disable CS8602 // Dereference of a possibly null reference. - k = e.Id; - string ePrefix = k.Substring(0, len); -#pragma warning restore CS8602 // Dereference of a possibly null reference. -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - long comp = long.Parse(ePrefix); - if (comp < start) - continue; // not there yet - if (k == lookForId) - { - return e; - } - if (ePrefix != fromPrefix) - throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return not exists."); - } - startPosition = k; // next batch will start from last entry - } - throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return not found."); - } - #endregion // FindAsync + // Releases the messages (work around). + async Task ReleaseAsync(RedisValue[] freeTargets) + { + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); + IDatabaseAsync db = conn.GetDatabase(); + try + { + await db.StreamClaimAsync(plan.FullUri(), + plan.ConsumerGroup, + RedisChannelConstants.NONE_CONSUMER, + 1, + freeTargets, + flags: CommandFlags.DemandMaster); + await Task.Delay(releaseDelay, cancellationToken); + if (releaseDelay < MAX_RELEASE_DELAY) + releaseDelay = Math.Min(releaseDelay * 2, MAX_RELEASE_DELAY); + + if (bachSize == options.BatchSize) + bachSize = 1; + else + bachSize = Math.Min(bachSize * 2, options.BatchSize); } #region Exception Handling - catch (Exception ex) + catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { - string key = plan.FullUri(); - _logger.LogError(ex.FormatLazy(), "{method} Failed: Entry [{entryId}] from [{key}] event stream", - mtdName, entryId, key); - throw; + await _connFactory.CreateConsumerGroupIfNotExistsAsync( + key, + plan.ConsumerGroup, + logger, cancellationToken); } - #endregion // Exception Handling + #endregion // Exception Handling } - #endregion // GetByIdAsync - - #region GetAsyncEnumerable - - /// - /// Gets asynchronous enumerable of announcements. - /// - /// The plan. - /// The options. - /// The cancellation token. - /// - async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable( - IConsumerPlan plan, - ConsumerAsyncEnumerableOptions? options, - [EnumeratorCancellation] CancellationToken cancellationToken) + #endregion // ReleaseAsync + } + + #endregion // SubsribeToSingleAsync + + #region GetByIdAsync + + /// + /// Gets announcement data by id. + /// + /// The entry identifier. + /// The plan. + /// The cancellation token. + /// + async ValueTask IConsumerChannelProvider.GetByIdAsync( + EventKey entryId, + IConsumerPlan plan, + CancellationToken cancellationToken) + { + string mtdName = $"{nameof(IConsumerChannelProvider)}.{nameof(IConsumerChannelProvider.GetByIdAsync)}"; + + try { IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); - var loop = AsyncLoop().WithCancellation(cancellationToken); - await foreach (StreamEntry entry in loop) + StreamEntry entry = await FindAsync(entryId); + + #region var announcement = new Announcement(...) + + Dictionary channelMeta = entry.Values.ToDictionary(m => m.Name, m => m.Value); + string channelType = GetMeta(nameof(MetadataExtensions.Empty.ChannelType)); + string id = GetMeta(nameof(MetadataExtensions.Empty.MessageId)); + string operation = GetMeta(nameof(MetadataExtensions.Empty.Operation)); + long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; + + #region string GetMeta(string propKey) + + string GetMeta(string propKey) { - if (cancellationToken.IsCancellationRequested) yield break; + string? result = channelMeta[propKey]; + if (result == null) throw new ArgumentNullException(propKey); + return result; + } - #region var announcement = new Announcement(...) + #endregion // string GetMeta(string propKey) - Dictionary channelMeta = entry.Values.ToDictionary(m => m.Name, m => m.Value); -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. + DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); #pragma warning disable CS8601 // Possible null reference assignment. - string id = channelMeta[nameof(MetadataExtensions.Empty.MessageId)]; - string operation = channelMeta[nameof(MetadataExtensions.Empty.Operation)]; - long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; - DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); - var meta = new Metadata - { - MessageId = id, - EventKey = entry.Id, - Environment = plan.Environment, - Uri = plan.Uri, - Operation = operation, - ProducedAt = producedAt - }; + var meta = new Metadata + { + MessageId = id, + EventKey = entry.Id, + Environment = plan.Environment, + Uri = plan.Uri, + Operation = operation, + ProducedAt = producedAt, + ChannelType = channelType + }; #pragma warning restore CS8601 // Possible null reference assignment. -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - var filter = options?.OperationFilter; - if (filter != null && !filter(meta)) - continue; - Bucket segmets = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Segments); - Bucket interceptions = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Interceptions); + Bucket segmets = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Segments); + Bucket interceptions = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Interceptions); - var announcement = new Announcement - { - Metadata = meta, - Segments = segmets, - InterceptorsData = interceptions - }; + var announcement = new Announcement + { + Metadata = meta, + Segments = segmets, + InterceptorsData = interceptions + }; - #endregion // var announcement = new Announcement(...) + #endregion // var announcement = new Announcement(...) - yield return announcement; - } + return announcement; - #region AsyncLoop + #region FindAsync - async IAsyncEnumerable AsyncLoop() + async Task FindAsync(EventKey entryId) { - string uri = plan.FullUri(); + string lookForId = (string)entryId; + string key = plan.FullUri(); + string originId = lookForId; + int len = originId.IndexOf('-'); + string fromPrefix = originId.Substring(0, len); + long start = long.Parse(fromPrefix); + string startPosition = (start - 1).ToString(); int iteration = 0; - RedisValue startPosition = options?.From ?? BEGIN_OF_STREAM; - TimeSpan delay = TimeSpan.Zero; - while (true) + for (int i = 0; i < READ_BY_ID_ITERATIONS; i++) // up to 1000 items { - if (cancellationToken.IsCancellationRequested) yield break; - iteration++; StreamEntry[] entries = await db.StreamReadAsync( - uri, + key, startPosition, READ_BY_ID_CHUNK_SIZE, CommandFlags.DemandMaster); if (entries.Length == 0) - { - if (options?.ExitWhenEmpty ?? true) yield break; - delay = await DelayIfEmpty(delay, cancellationToken); - continue; - } + throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return nothing, start at ({startPosition}, iteration = {iteration})."); string k = string.Empty; foreach (StreamEntry e in entries) { - if (cancellationToken.IsCancellationRequested) yield break; - #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8602 // Dereference of a possibly null reference. k = e.Id; + string ePrefix = k.Substring(0, len); +#pragma warning restore CS8602 // Dereference of a possibly null reference. #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - if (options?.To != null && string.Compare(options?.To, k) < 0) - yield break; - yield return e; + long comp = long.Parse(ePrefix); + if (comp < start) + continue; // not there yet + if (k == lookForId) + { + return e; + } + if (ePrefix != fromPrefix) + throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return not exists."); } startPosition = k; // next batch will start from last entry } + throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return not found."); } - #endregion // AsyncLoop + #endregion // FindAsync + } + #region Exception Handling + + catch (Exception ex) + { + string key = plan.FullUri(); + _logger.LogError(ex.FormatLazy(), "{method} Failed: Entry [{entryId}] from [{key}] event stream", + mtdName, entryId, key); + throw; + } + + #endregion // Exception Handling + } + + #endregion // GetByIdAsync + + #region GetAsyncEnumerable + + /// + /// Gets asynchronous enumerable of announcements. + /// + /// The plan. + /// The options. + /// The cancellation token. + /// + async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable( + IConsumerPlan plan, + ConsumerAsyncEnumerableOptions? options, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); + IDatabaseAsync db = conn.GetDatabase(); + var loop = AsyncLoop().WithCancellation(cancellationToken); + await foreach (StreamEntry entry in loop) + { + if (cancellationToken.IsCancellationRequested) yield break; + + #region var announcement = new Announcement(...) + + Dictionary channelMeta = entry.Values.ToDictionary(m => m.Name, m => m.Value); +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8601 // Possible null reference assignment. + string id = channelMeta[nameof(MetadataExtensions.Empty.MessageId)]; + string operation = channelMeta[nameof(MetadataExtensions.Empty.Operation)]; + long producedAtUnix = (long)channelMeta[nameof(MetadataExtensions.Empty.ProducedAt)]; + DateTimeOffset producedAt = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix); + var meta = new Metadata + { + MessageId = id, + EventKey = entry.Id, + Environment = plan.Environment, + Uri = plan.Uri, + Operation = operation, + ProducedAt = producedAt + }; +#pragma warning restore CS8601 // Possible null reference assignment. +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. + var filter = options?.OperationFilter; + if (filter != null && !filter(meta)) + continue; + + Bucket segmets = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Segments); + Bucket interceptions = await GetBucketAsync(plan, channelMeta, meta, EventBucketCategories.Interceptions); + + var announcement = new Announcement + { + Metadata = meta, + Segments = segmets, + InterceptorsData = interceptions + }; + + #endregion // var announcement = new Announcement(...) + + yield return announcement; } - #endregion // GetAsyncEnumerable - - #region ValueTask GetBucketAsync(StorageType storageType) // local function - - /// - /// Gets a data bucket. - /// - /// The plan. - /// The channel meta. - /// The meta. - /// Type of the storage. - /// - private async ValueTask GetBucketAsync( - IConsumerPlan plan, - Dictionary channelMeta, - Metadata meta, - EventBucketCategories storageType) + #region AsyncLoop + + async IAsyncEnumerable AsyncLoop() { + string uri = plan.FullUri(); - IEnumerable strategies = await plan.StorageStrategiesAsync; - strategies = strategies.Where(m => m.IsOfTargetType(storageType)); - Bucket bucket = Bucket.Empty; - if (strategies.Any()) + int iteration = 0; + RedisValue startPosition = options?.From ?? BEGIN_OF_STREAM; + TimeSpan delay = TimeSpan.Zero; + while (true) { - foreach (var strategy in strategies) + if (cancellationToken.IsCancellationRequested) yield break; + + iteration++; + StreamEntry[] entries = await db.StreamReadAsync( + uri, + startPosition, + READ_BY_ID_CHUNK_SIZE, + CommandFlags.DemandMaster); + if (entries.Length == 0) { - bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + if (options?.ExitWhenEmpty ?? true) yield break; + delay = await DelayIfEmpty(delay, cancellationToken); + continue; } + string k = string.Empty; + foreach (StreamEntry e in entries) + { + if (cancellationToken.IsCancellationRequested) yield break; + +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. + k = e.Id; +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. + if (options?.To != null && string.Compare(options?.To, k) < 0) + yield break; + yield return e; + } + startPosition = k; // next batch will start from last entry } - else + } + + #endregion // AsyncLoop + } + + #endregion // GetAsyncEnumerable + + #region ValueTask GetBucketAsync(StorageType storageType) // local function + + /// + /// Gets a data bucket. + /// + /// The plan. + /// The channel meta. + /// The meta. + /// Type of the storage. + /// + private async ValueTask GetBucketAsync( + IConsumerPlan plan, + Dictionary channelMeta, + Metadata meta, + EventBucketCategories storageType) + { + + IEnumerable strategies = await plan.StorageStrategiesAsync; + strategies = strategies.Where(m => m.IsOfTargetType(storageType)); + Bucket bucket = Bucket.Empty; + if (strategies.Any()) + { + foreach (var strategy in strategies) { - bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } + } + else + { + bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + } - return bucket; + return bucket; #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning disable CS8603 // Possible null reference return. - string LocalGetProperty(string k) => (string)channelMeta[k]; + string LocalGetProperty(string k) => (string)channelMeta[k]; #pragma warning restore CS8603 // Possible null reference return. #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - } + } - #endregion // ValueTask StoreBucketAsync(StorageType storageType) // local function + #endregion // ValueTask StoreBucketAsync(StorageType storageType) // local function - #region DelayIfEmpty + #region DelayIfEmpty - // avoiding system hit when empty (mitigation of self DDoS) - private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationToken cancellationToken) - { - var cfg = _setting.DelayWhenEmptyBehavior; - var newDelay = cfg.CalcNextDelay(previousDelay, cfg); - var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); - newDelay = TimeSpan.FromMilliseconds(limitDelay); - await Task.Delay(newDelay, cancellationToken); - return newDelay; - } + // avoiding system hit when empty (mitigation of self DDoS) + private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationToken cancellationToken) + { + var cfg = _setting.DelayWhenEmptyBehavior; + var newDelay = cfg.CalcNextDelay(previousDelay, cfg); + var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); + newDelay = TimeSpan.FromMilliseconds(limitDelay); + await Task.Delay(newDelay, cancellationToken); + return newDelay; + } - #endregion // DelayIfEmpty + #endregion // DelayIfEmpty - #region GetKeysUnsafeAsync + #region GetKeysUnsafeAsync - /// - /// Gets the keys unsafe asynchronous. - /// - /// The pattern. - /// The cancellation token. - /// - public async IAsyncEnumerable GetKeysUnsafeAsync( - string pattern, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + /// + /// Gets the keys unsafe asynchronous. + /// + /// The pattern. + /// The cancellation token. + /// + public async IAsyncEnumerable GetKeysUnsafeAsync( + string pattern, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + IConnectionMultiplexer multiplexer = await _connFactory.GetAsync(cancellationToken); + var distict = new HashSet(); + while (!cancellationToken.IsCancellationRequested) { - IConnectionMultiplexer multiplexer = await _connFactory.GetAsync(cancellationToken); - var distict = new HashSet(); - while (!cancellationToken.IsCancellationRequested) + foreach (EndPoint endpoint in multiplexer.GetEndPoints()) { - foreach (EndPoint endpoint in multiplexer.GetEndPoints()) - { - IServer server = multiplexer.GetServer(endpoint); - // TODO: [bnaya 2020_09] check the pagination behavior + IServer server = multiplexer.GetServer(endpoint); + // TODO: [bnaya 2020_09] check the pagination behavior #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning disable CS8604 // Possible null reference argument. - await foreach (string key in server.KeysAsync(pattern: pattern)) - { - if (distict.Contains(key)) - continue; - distict.Add(key); - yield return key; - } + await foreach (string key in server.KeysAsync(pattern: pattern)) + { + if (distict.Contains(key)) + continue; + distict.Add(key); + yield return key; + } #pragma warning restore CS8604 // Possible null reference argument. #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. - } } } - - #endregion // GetKeysUnsafeAsync } + + #endregion // GetKeysUnsafeAsync } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs new file mode 100644 index 00000000..614e06eb --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs @@ -0,0 +1,12 @@ +namespace EventSourcing.Backbone.Channels; + +/// +/// Constants +/// +internal static class ProducerChannelConstants +{ + /// + /// The name of redis producer channel source + /// + public const string REDIS_CHANNEL_SOURCE = "redis-producer-channel"; +} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 07ba6a97..c4136276 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -2,6 +2,8 @@ using System.Diagnostics; using System.Text.Json; +using EventSourcing.Backbone.Private; + using Microsoft.Extensions.Logging; using OpenTelemetry; @@ -16,7 +18,7 @@ namespace EventSourcing.Backbone.Channels.RedisProvider; internal class RedisProducerChannel : IProducerChannelProvider { - private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); + private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(ProducerChannelConstants.REDIS_CHANNEL_SOURCE); private readonly ILogger _logger; private readonly AsyncPolicy _resiliencePolicy; private readonly IEventSourceRedisConnectionFactory _connFactory; diff --git a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs index 73d60bae..58cc8d13 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs @@ -1,35 +1,27 @@ -using System.Text.Json; +using System.Diagnostics; +using System.Text.Json; using System.Text.Json.Serialization; -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone; + +/// +/// Constants +/// +public static class EventSourceConstants { - /// - /// Constants - /// - public static class EventSourceConstants - { - /// - /// The name of redis consumer channel source - /// - public const string REDIS_CONSUMER_CHANNEL_SOURCE = "redis-consumer-channel"; - /// - /// The name of redis producer channel source - /// - public const string REDIS_PRODUCER_CHANNEL_SOURCE = "redis-producer-channel"; - public static readonly JsonStringEnumConverter EnumConvertor = new JsonStringEnumConverter(JsonNamingPolicy.CamelCase); - public static readonly JsonSerializerOptions SerializerOptionsWithIndent = new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - // PropertyNameCaseInsensitive = true, - // IgnoreNullValues = true, - WriteIndented = true, - Converters = - { - EnumConvertor, - JsonMemoryBytesConverterFactory.Default - } - }; - } + public static readonly JsonStringEnumConverter EnumConvertor = new JsonStringEnumConverter(JsonNamingPolicy.CamelCase); + public static readonly JsonSerializerOptions SerializerOptionsWithIndent = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + // PropertyNameCaseInsensitive = true, + // IgnoreNullValues = true, + WriteIndented = true, + Converters = + { + EnumConvertor, + JsonMemoryBytesConverterFactory.Default + } + }; } diff --git a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs index fe134167..d286a5e5 100644 --- a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs +++ b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs @@ -9,16 +9,16 @@ namespace EventSourcing.Backbone; public static class EventSourcingExtensions { - /// - /// Adds the event consumer telemetry source (will result in tracing the consumer). - /// - /// The builder. - /// - public static TracerProviderBuilder ListenToEventSourceRedisChannel( - this TracerProviderBuilder builder) => - builder.AddSource( - EventSourceConstants.REDIS_CONSUMER_CHANNEL_SOURCE, - EventSourceConstants.REDIS_PRODUCER_CHANNEL_SOURCE); + ///// + ///// Adds the event consumer telemetry source (will result in tracing the consumer). + ///// + ///// The builder. + ///// + //public static TracerProviderBuilder ListenToEventSourceRedisChannel( + // this TracerProviderBuilder builder) => + // builder.AddSource( + // EventSourceConstants.Consumer.REDIS_CHANNEL_SOURCE, + // EventSourceConstants.Producer.REDIS_CHANNEL_SOURCE); #region ExtractSpan diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index f26ab58e..c5a297be 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -9,6 +9,11 @@ README.md + + + + + \ @@ -19,6 +24,9 @@ + + + @@ -31,5 +39,9 @@ + + + + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index dcbe31d3..49a00b7d 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -1,4 +1,5 @@ using EventSourcing.Backbone; +using EventSourcing.Backbone.Channels; using Microsoft.Extensions.Hosting; @@ -11,29 +12,21 @@ // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 // see: // https://opentelemetry.io/docs/instrumentation/net/getting-started/ +// https://opentelemetry.io/docs/demo/services/cart/ // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables - - +// https://opentelemetry.io/docs/demo/docker-deployment/ namespace Microsoft.Extensions.DependencyInjection; - /// /// core extensions for ASP.NET Core /// public static class EventSourcingOtel { - /// - /// The name of redis consumer channel source - /// - public const string REDIS_CONSUMER_CHANNEL_SOURCE = "redis-consumer-channel"; - /// - /// The name of redis producer channel source - /// - public const string REDIS_PRODUCER_CHANNEL_SOURCE = "redis-producer-channel"; - #region WithEventSourcingTracing + #region Overloads + /// /// Adds the open-telemetry tracing binding. /// @@ -50,6 +43,8 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( return builder.WithEventSourcingTracing(env, injection); } + #endregion // Overloads + /// /// Adds the open-telemetry tracing binding. /// @@ -66,8 +61,8 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( .WithTracing(tracerProviderBuilder => { var sources = new[] { (string)env, - REDIS_CONSUMER_CHANNEL_SOURCE, - REDIS_PRODUCER_CHANNEL_SOURCE}; + ProducerChannelConstants.REDIS_CHANNEL_SOURCE, + ConsumerChannelConstants.REDIS_CHANNEL_SOURCE}; tracerProviderBuilder .AddSource(sources) @@ -83,6 +78,8 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( #region WithEventSourcingMetrics + #region Overloads + /// /// Adds the open-telemetry metrics binding. /// @@ -99,6 +96,8 @@ public static OpenTelemetryBuilder WithEventSourcingMetrics( return builder.WithEventSourcingMetrics(env, injection); } + #endregion // Overloads + /// /// Adds the open-telemetry metrics binding. /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs index ff9015cc..bb3b14c6 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs @@ -20,6 +20,7 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; #pragma warning disable S3881 // "IDisposable" should be implemented correctly +#pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest @@ -38,9 +39,7 @@ public class AckPatternsTests : TestsBase private readonly string ENV = $"test"; protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; - private const int TIMEOUT = 1_000 * 50; #region Ctor diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index b31e7170..1c2d487b 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -168,7 +168,10 @@ public async Task Environmet_Test() #endregion // await using IConsumerLifetime subscription = ...Subscribe(...) + var sw = Stopwatch.StartNew(); await subscription.Completion; + sw.Stop(); + _outputHelper.WriteLine($"Consume Duration = {sw.Elapsed:mm\\:ss\\.ff}"); #region Validation diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index 97f9bd3a..6082ef8a 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -43,91 +43,6 @@ public static IConnectionMultiplexer AddRedis( #endregion // AddRedis - #region AddOpenTelemetry - - /// - /// Adds the open-telemetry binding. - /// - /// The services. - /// The host env. - /// Short name of the application. - /// The redis connection. - /// - public static IServiceCollection AddOpenTelemetry( - this IServiceCollection services, - IHostEnvironment hostEnv, - string shortAppName, - IConnectionMultiplexer redisConnection) - { - // see: - // https://opentelemetry.io/docs/instrumentation/net/getting-started/ - // https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Jaeger/README.md#environment-variables - - Console.WriteLine($"JAEGER endpoint: key='OTEL_EXPORTER_JAEGER_ENDPOINT', env='{hostEnv.EnvironmentName}'"); // will be visible in the pods logs - -#pragma warning disable S125 // Sections of code should not be commented out - services.AddOpenTelemetry() - .WithTracing(builder => - { - builder.SetResourceBuilder(ResourceBuilder.CreateDefault() - .AddService(shortAppName)) - .ListenToEventSourceRedisChannel() - // .SetSampler() - .AddAspNetCoreInstrumentation(m => - { - m.Filter = OpenTelemetryFilter; - // m.Enrich - m.RecordException = true; - m.EnableGrpcAspNetCoreSupport = true; - }) - .AddHttpClientInstrumentation(m => - { - // m.Enrich - m.RecordException = true; - }) - //.AddRedisInstrumentation(redisConnection - // //, m => { - // // m.FlushInterval - // //} - // ) - .AddOtlpExporter() - .SetSampler(TestSampler.Create(LogLevel.Information)); - - if (hostEnv.IsDevelopment()) - { - builder.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Console); - } - }); - return services; -#pragma warning restore S125 // Sections of code should not be commented out - - #region OpenTelemetryFilter - - bool OpenTelemetryFilter(HttpContext context) => OpenTelemetryFilterMap(context.Request.Path.Value); - - bool OpenTelemetryFilterMap(string? path) - { - if (string.IsNullOrEmpty(path) || - path == "/health" || - path == "/readiness" || - path == "/version" || - path == "/settings" || - path.StartsWith("/v1/kv/") || // configuration - path == "/api/v2/write" || // influx metrics - path == "/_bulk" || - path.StartsWith("/swagger") || - path.IndexOf("health-check") != -1) - { - return false; - } - return true; - } - - #endregion // OpenTelemetryFilter - } - - #endregion // AddOpenTelemetry - #region WithJsonOptions /// diff --git a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs index 4451b47c..41e39697 100644 --- a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs +++ b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs @@ -51,8 +51,8 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati cfg.AddAspNetCoreInstrumentation( /* m => m.Filter = filter */) .AddOtlpExporter() .AddPrometheusExporter(); - if (environment.IsDevelopment()) - cfg.AddConsoleExporter(); + //if (environment.IsDevelopment()) + // cfg.AddConsoleExporter(); }); return services; From 9b064d72b828f63f8c811c7c07cb0a4260e250cc Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:30:23 +0300 Subject: [PATCH 171/178] feat: telemetry --- .editorconfig | 3 + .../ConsumerChannelConstants.cs | 2 +- .../RedisConsumerChannel.cs | 198 +++++++++++------- .../RedisHashStorageStrategy.cs | 5 + .../Telemetry.cs | 14 ++ .../ProducerChannelConstants.cs | 2 +- .../RedisProducerChannel.cs | 27 ++- .../RedisHashStorageStrategy.cs | 5 + .../Telemetry.cs | 10 + .../EventSourceRedisConnectionFactory.cs | 11 +- .../RedisChannelConstants.cs | 40 ++-- .../RedisCommonProviderExtensions.cs | 102 ++++++--- .../RedisTelemetryExtensions.cs | 30 --- .../Telemetry.cs | 11 + .../S3ConsumerStorageStrategy.cs | 5 + .../S3ProducerStorageStrategy.cs | 5 + .../Builder/FilteredStorageStrategy.cs | 5 + .../ConsumerTelemetryExtensions.cs | 41 ++++ .../Consumer/Ack/Ack.cs | 8 +- .../Consumer/Ack/AckOnce.cs | 25 ++- .../Consumer/Ack/IAckOperations.cs | 11 +- .../Consumer/ConsumerMetadata.cs | 11 +- .../Consumer/ConsumerOptions.cs | 10 - .../Provider/IConsumerStorageStrategy.cs | 5 + .../EventSourceConstants.cs | 3 +- .../Extensions/EventSourcingExtensions.cs | 64 ------ .../Extensions/TelemetryrExtensions.cs | 47 ----- .../Producer/ProducerExtensions.cs | 6 + .../Provider/IProducerStorageStrategy.cs | 5 + .../EventSourceTelemetryExtensions.cs | 152 ++++++++++++++ .../Telemetry/ITagAddition.cs | 6 + .../Telemetry/TagAddition.cs | 22 ++ .../deprecated/ITelemetryAbstraction.cs | 7 + .../deprecated/TelemetryAbstraction.cs | 91 ++++++++ ...g.Backbone.OpenTelemetry.Extensions.csproj | 1 + .../EventSourcingOtel.cs | 11 +- .../Builder/FilteredStorageStrategy.cs | 6 +- .../ProducerDefaultSegmentationStrategy.cs | 31 ++- .../ProducerTelemetryExtensions.cs | 35 ++++ Tests/ConsoleTest/Constants.cs | 2 +- Tests/ConsoleTest/IFoo.cs | 8 +- Tests/ConsoleTest/OpenTelemetryExtensions.cs | 2 +- Tests/ConsoleTest/Program.cs | 23 +- Tests/ConsoleTest/Worker.cs | 7 - .../AckPatternsTests.cs | 5 - .../EndToEndStressTests.cs | 8 - .../EndToEndTests.cs | 6 - .../InheritanceTests.cs | 9 +- .../MigrationTest.cs | 7 - .../AspCoreExtensions.cs | 4 - .../Controllers/EventSourceApiController.cs | 2 - ...EventSourcing.Backbone.WebEventTest.csproj | 4 + .../OpenTelemetryExtensions.cs | 29 ++- .../Extensions/ConsumerExtensions.cs | 1 - .../Program.cs | 4 +- .../RegisterEventSourceExtensions.cs | 2 - .../Extensions/ConsumerExtensions.cs | 1 - .../Extensions/ConsumerExtensions.cs | 1 - .../Extensions/OpenTelemetryExtensions.cs | 19 +- Tests/WebSampleS3/WebSampleS3.csproj | 4 + 60 files changed, 802 insertions(+), 419 deletions(-) create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs create mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs create mode 100644 Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs delete mode 100644 EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs delete mode 100644 EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs create mode 100644 EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs create mode 100644 EventSourcing.Backbone.Abstractions/Telemetry/ITagAddition.cs create mode 100644 EventSourcing.Backbone.Abstractions/Telemetry/TagAddition.cs create mode 100644 EventSourcing.Backbone.Abstractions/Telemetry/deprecated/ITelemetryAbstraction.cs create mode 100644 EventSourcing.Backbone.Abstractions/Telemetry/deprecated/TelemetryAbstraction.cs create mode 100644 Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs diff --git a/.editorconfig b/.editorconfig index 7c692a83..0e6434b4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,6 @@ # HAA0101: Array allocation for params parameter dotnet_diagnostic.HAA0101.severity = none + +# HAA0601: Value type to reference type conversion causing boxing allocation +dotnet_diagnostic.HAA0601.severity = none diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs index fedf4698..5d0470a7 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs @@ -8,5 +8,5 @@ internal static class ConsumerChannelConstants /// /// The name of redis consumer channel source /// - public const string REDIS_CHANNEL_SOURCE = "redis-consumer-channel"; + public const string REDIS_CHANNEL_SOURCE = "event-source-redis-consumer-channel"; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index c34d0fa0..e409f380 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -1,10 +1,12 @@ using System.Diagnostics; +using System.Diagnostics.Metrics; using System.Net; using System.Runtime.CompilerServices; using System.Text.Json; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider.Common; +using EventSourcing.Backbone.Consumers; using EventSourcing.Backbone.Private; using Microsoft.Extensions.Logging; @@ -13,6 +15,7 @@ using static System.Math; using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Telemetry; // TODO: [bnaya 2021-07] MOVE TELEMETRY TO THE BASE CLASSES OF PRODUCER / CONSUME @@ -23,6 +26,16 @@ namespace EventSourcing.Backbone.Channels.RedisProvider; /// internal class RedisConsumerChannel : IConsumerChannelProvider { + private static readonly Counter StealAmountCounter = Metrics.CreateCounter("event-source.consumer.message-stealing", "sum"); + private static readonly Counter ConcumeBatchCountCounter = Metrics.CreateCounter("event-source.consumer.batch", "count", + "count of the number of consuming batches form the stream provider"); + private static readonly Counter ConcumeBatchSumCounter = Metrics.CreateCounter("event-source.consumer.batch", "sum", + "count of the messages consuming before process"); + private static readonly Counter ConcumeBatchFailureCounter = Metrics.CreateCounter("event-source.consumer.batch.failure", "count", + "batch reading failure"); + private static readonly Counter AckCounter = Metrics.CreateCounter("event-source.consumer.ack", "count", + "Acknowledge count"); + private const string BEGIN_OF_STREAM = "0000000000000"; /// /// The read by identifier chunk size. @@ -34,7 +47,6 @@ internal class RedisConsumerChannel : IConsumerChannelProvider /// Receiver max iterations /// private const int READ_BY_ID_ITERATIONS = 1000 / READ_BY_ID_CHUNK_SIZE; - private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); private readonly ILogger _logger; private readonly RedisConsumerChannelSetting _setting; @@ -235,11 +247,14 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( TimeSpan delay = TimeSpan.Zero; int emptyBatchCount = 0; - while (!cancellationToken.IsCancellationRequested) + using (Track.StartActivity("event-source.consumer.loop", ActivityKind.Server)) { - var proceed = await HandleBatchAsync(); - if (!proceed) - break; + while (!cancellationToken.IsCancellationRequested) + { + var proceed = await HandleBatchAsync(); + if (!proceed) + break; + } } #region HandleBatchAsync @@ -253,7 +268,6 @@ async ValueTask HandleBatchAsync() #endregion // HandleBatchAsync - #region HandleBatchBreakerAsync async Task HandleBatchBreakerAsync(CancellationToken ct) @@ -272,7 +286,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) } ct.ThrowIfCancellationRequested(); - + try { var batchCancellation = new CancellationTokenSource(); @@ -287,10 +301,10 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) for (; i < results.Length && !batchCancellation.IsCancellationRequested; i++) { StreamEntry result = results[i]; - #region Metadata meta = ... Dictionary channelMeta = result.Values.ToDictionary(m => m.Name, m => m.Value); + Metadata meta; string? metaJson = channelMeta[META_SLOT]; string eventKey = ((string?)result.Id) ?? throw new ArgumentException(nameof(MetadataExtensions.Empty.EventKey)); @@ -333,13 +347,50 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) #endregion // Metadata meta = ... + ActivityContext parentContext = EventSourceTelemetryExtensions.ExtractSpan(channelMeta, ExtractTraceContext); + using var activity = Track.StartConsumerTrace(meta, parentContext); + + #region IEnumerable ExtractTraceContext(Dictionary entries, string key) + + IEnumerable ExtractTraceContext(Dictionary entries, string key) + { + try + { + if (entries.TryGetValue(key, out var value)) + { + if (string.IsNullOrEmpty(value)) + return Array.Empty(); + return new[] { value.ToString() }; + } + } + #region Exception Handling + + catch (Exception ex) + { + Exception err = ex.FormatLazy(); + _logger.LogError(err, "Failed to extract trace context: {error}", err); + } + + #endregion // Exception Handling + + return Enumerable.Empty(); + } + + #endregion // IEnumerable ExtractTraceContext(Dictionary entries, string key) + int local = i; var cancellableIds = results[local..].Select(m => m.Id); var ack = new AckOnce( - () => AckAsync(result.Id), + async (cause) => + { + Activity.Current?.AddEvent("event-source.consumer.event.ack", t => t.Add("cause", cause)); + await AckAsync(result.Id); + }, plan.Options.AckBehavior, logger, - async () => + async (cause) => { + AckCounter.Add(1); + Activity.Current?.AddEvent("event-source.consumer.event.cancel", t => t.Add("cause", cause)); batchCancellation.CancelSafe(); // cancel forward await CancelAsync(cancellableIds); }); @@ -374,52 +425,12 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) #endregion // var announcement = new Announcement(...) - #region Start Telemetry Span - - ActivityContext parentContext = meta.ExtractSpan(channelMeta, ExtractTraceContext); - // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - var activityName = $"{meta.Operation} consume"; - - bool traceAsParent = (DateTimeOffset.UtcNow - meta.ProducedAt) < plan.Options.TraceAsParent; - ActivityContext parentActivityContext = traceAsParent ? parentContext : default; - using var activity = ACTIVITY_SOURCE.StartActivity( - activityName, - ActivityKind.Consumer, - parentActivityContext, links: new[] { new ActivityLink(parentContext) }); - meta.InjectTelemetryTags(activity); - - #region IEnumerable ExtractTraceContext(Dictionary entries, string key) - - IEnumerable ExtractTraceContext(Dictionary entries, string key) + bool succeed; + using (var execActivity = Track.StartInternalTrace("event-source.consumer.execute-event")) { - try - { - if (entries.TryGetValue(key, out var value)) - { - if (string.IsNullOrEmpty(value)) - return Array.Empty(); - return new[] { value.ToString() }; - } - } - #region Exception Handling - - catch (Exception ex) - { - Exception err = ex.FormatLazy(); - _logger.LogError(err, "Failed to extract trace context: {error}", err); - } - - #endregion // Exception Handling - - return Enumerable.Empty(); + succeed = await func(announcement, ack); + execActivity?.SetTag("succeed", succeed); } - - #endregion // IEnumerable ExtractTraceContext(Dictionary entries, string key) - - #endregion // Start Telemetry Span - - bool succeed = await func(announcement, ack); if (succeed) { releaseDelay = INIT_RELEASE_DELAY; @@ -430,9 +441,16 @@ IEnumerable ExtractTraceContext(Dictionary entri // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) { - RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); - await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet - await Task.Delay(releaseDelay, ct); + using (Track.StartInternalTrace("event-source.consumer.release-events-on-failure")) + { + RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); + await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet + //using (Track.StartInternalTrace("event-source.consumer.delay", t => t.Add("duration", releaseDelay))) + //{ + // // let other potential consumer the chance of getting ownership + // await Task.Delay(releaseDelay, ct); + //} + } } } } @@ -463,25 +481,31 @@ async Task ReadBatchAsync() if (values.Length == 0) { isFirstBatchOrFailure = false; + string group = plan.ConsumerGroup; + using var activity = Track.StartInternalTrace("event-source.consumer.read-batch", t => t.Add("consumer-group", group)); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { + ConcumeBatchCountCounter.Add(1); values = await db.StreamReadGroupAsync( key, - plan.ConsumerGroup, + group, plan.ConsumerName, position: StreamPosition.NewMessages, count: bachSize, flags: flags) .WithCancellation(ct, () => Array.Empty()) .WithCancellation(cancellationToken, () => Array.Empty()); + activity?.SetTag("count", values.Length); + ConcumeBatchSumCounter.Add(values.Length); } #region Exception Handling catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { + ConcumeBatchFailureCounter.Add(1); logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, @@ -490,6 +514,7 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( } catch (RedisServerException ex) { + ConcumeBatchFailureCounter.Add(1); logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( key, @@ -527,10 +552,12 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( // Check for pending messages of the current consumer (crash scenario) async Task ReadSelfPending() { + StreamEntry[] values = Array.Empty(); if (!isFirstBatchOrFailure) return values; + using var _ = Track.StartInternalTrace("event-source.consumer.self-pending"); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); @@ -590,17 +617,26 @@ async Task ClaimStaleMessages( if (values.Length != 0) return values; if (emptyBatchCount < claimingTrigger.EmptyBatchCount) return values; + using var _ = Track.StartInternalTrace("event-source.consumer.stale-events"); try { IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); - StreamPendingInfo pendingInfo = await db.StreamPendingAsync(key, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); + StreamPendingInfo pendingInfo; + using (Track.StartInternalTrace("event-source.consumer.events-stealing.pending")) + { + pendingInfo = await db.StreamPendingAsync(key, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); + } foreach (var c in pendingInfo.Consumers) { var self = c.Name == plan.ConsumerName; if (self) continue; try { - var pendMsgInfo = await db.StreamPendingMessagesAsync( + StreamPendingMessageInfo[] pendMsgInfo; + using (Track.StartInternalTrace("event-source.consumer.events-stealing.pending-events", + t => t.Add("from-consumer", c.Name))) + { + pendMsgInfo = await db.StreamPendingMessagesAsync( key, plan.ConsumerGroup, 10, @@ -608,8 +644,7 @@ async Task ClaimStaleMessages( pendingInfo.LowestPendingMessageId, pendingInfo.HighestPendingMessageId, flags: CommandFlags.DemandMaster); - - + } RedisValue[] ids = pendMsgInfo .Where(x => x.IdleTimeInMilliseconds > minIdleTime) @@ -618,17 +653,24 @@ async Task ClaimStaleMessages( continue; #region Log - logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} messages, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); + logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} events, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); #endregion // Log - // will claim messages only if older than _setting.ClaimingTrigger.MinIdleTime - values = await db.StreamClaimAsync(key, + int count = ids.Length; + StealAmountCounter.WithTag("from-consumer", c.Name).Add(count); + // will claim events only if older than _setting.ClaimingTrigger.MinIdleTime + using (Track.StartInternalTrace("event-source.consumer.events-stealing.claim", + t => t.Add("from-consumer", c.Name) + .Add("message-count", count))) + { + values = await db.StreamClaimAsync(key, plan.ConsumerGroup, c.Name, minIdleTime, ids, flags: CommandFlags.DemandMaster); + } if (values.Length != 0) logger.LogInformation("Event Source Consumer [{name}]: Claimed {count} messages, from Consumer [{name}]", plan.ConsumerName, c.PendingMessageCount, c.Name); } @@ -731,13 +773,20 @@ async Task ReleaseAsync(RedisValue[] freeTargets) IDatabaseAsync db = conn.GetDatabase(); try { - await db.StreamClaimAsync(plan.FullUri(), + using (Track.StartInternalTrace("event-source.consumer.release-ownership", t => t.Add("consumer-group", plan.ConsumerGroup))) + { + await db.StreamClaimAsync(plan.FullUri(), plan.ConsumerGroup, RedisChannelConstants.NONE_CONSUMER, 1, freeTargets, flags: CommandFlags.DemandMaster); - await Task.Delay(releaseDelay, cancellationToken); + } + using (Track.StartInternalTrace("event-source.consumer.delay", t => t.Add("delay", releaseDelay))) + { + // let other potential consumer the chance of getting ownership + await Task.Delay(releaseDelay, cancellationToken); + } if (releaseDelay < MAX_RELEASE_DELAY) releaseDelay = Math.Min(releaseDelay * 2, MAX_RELEASE_DELAY); @@ -1027,12 +1076,18 @@ private async ValueTask GetBucketAsync( { foreach (var strategy in strategies) { - bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + using (Track.StartInternalTrace($"event-source.consumer.{strategy.Name}-storage.{storageType}.get")) + { + bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + } } } else { - bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + using (Track.StartInternalTrace($"event-source.consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) + { + bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); + } } return bucket; @@ -1055,7 +1110,10 @@ private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationTo var newDelay = cfg.CalcNextDelay(previousDelay, cfg); var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); newDelay = TimeSpan.FromMilliseconds(limitDelay); - await Task.Delay(newDelay, cancellationToken); + using (Track.StartInternalTrace("event-source.consumer.delay.when-empty-queue", t => t.Add("delay", newDelay))) + { + await Task.Delay(newDelay, cancellationToken); + } return newDelay; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs index 2ce61351..11d441a9 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -22,6 +22,11 @@ public RedisHashStorageStrategy(IEventSourceRedisConnectionFactory connFactory) _connFactory = connFactory; } + /// + /// Gets the name of the storage provider. + /// + public string Name { get; } = "Redis"; + /// /// Load the bucket information. /// diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs new file mode 100644 index 00000000..5766891f --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; +using System.Diagnostics.Metrics; + +namespace EventSourcing.Backbone.Channels.RedisProvider; + +internal class Telemetry +{ + public readonly static Meter Metrics = new(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); + public static readonly ActivitySource Track = new ActivitySource(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); + + //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", + // ""); + +} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs index 614e06eb..c25da270 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs @@ -8,5 +8,5 @@ internal static class ProducerChannelConstants /// /// The name of redis producer channel source /// - public const string REDIS_CHANNEL_SOURCE = "redis-producer-channel"; + public const string REDIS_CHANNEL_SOURCE = "event-source-redis-producer-channel"; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index c4136276..d42954f7 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using System.Text.Json; -using EventSourcing.Backbone.Private; +using EventSourcing.Backbone.Producers; using Microsoft.Extensions.Logging; @@ -14,11 +14,12 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Telemetry; + namespace EventSourcing.Backbone.Channels.RedisProvider; internal class RedisProducerChannel : IProducerChannelProvider { - private static readonly ActivitySource ACTIVITY_SOURCE = new ActivitySource(ProducerChannelConstants.REDIS_CHANNEL_SOURCE); private readonly ILogger _logger; private readonly AsyncPolicy _resiliencePolicy; private readonly IEventSourceRedisConnectionFactory _connFactory; @@ -65,6 +66,9 @@ public async ValueTask SendAsync( { Metadata meta = payload.Metadata; string id = meta.MessageId; + using var activity = Track.StartInternalTrace($"event-source.producer.{meta.Operation}.process", + t => t.Add("env", meta.Environment) + .Add("message-id", id)); #region var entries = new NameValueEntry[]{...} @@ -95,10 +99,8 @@ async Task LocalStreamAddAsync() await LocalStoreBucketAsync(EventBucketCategories.Interceptions); var telemetryBuilder = commonEntries.ToBuilder(); - var activityName = $"{meta.Operation} produce"; - using Activity? activity = ACTIVITY_SOURCE.StartActivity(activityName, ActivityKind.Producer); - activity.InjectSpan(meta, telemetryBuilder, LocalInjectTelemetry); - meta.InjectTelemetryTags(activity); + using Activity? activity = Track.StartProducerTrace(meta); + activity.InjectSpan(telemetryBuilder, LocalInjectTelemetry); var entries = telemetryBuilder.ToArray(); try @@ -148,17 +150,20 @@ async ValueTask LocalStoreBucketAsync(EventBucketCategories storageType) async ValueTask SaveBucketAsync(IProducerStorageStrategy strategy) { - IImmutableDictionary metaItems = - await strategy.SaveBucketAsync(id, bucket, storageType, meta); - foreach (var item in metaItems) + using (Track.StartInternalTrace($"event-source.producer.{strategy.Name}-storage.{storageType}.set")) { - commonEntries = commonEntries.Add(KV(item.Key, item.Value)); + IImmutableDictionary metaItems = + await strategy.SaveBucketAsync(id, bucket, storageType, meta); + foreach (var item in metaItems) + { + commonEntries = commonEntries.Add(KV(item.Key, item.Value)); + } } - } } #endregion // ValueTask StoreBucketAsync(StorageType storageType) // local function + #region LocalInjectTelemetry void LocalInjectTelemetry( diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs index c332d2cf..82518f7f 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/StorageStrategies/RedisHashStorageStrategy.cs @@ -35,6 +35,11 @@ public RedisHashStorageStrategy( #endregion // Ctor + /// + /// Gets the name of the storage provider. + /// + public string Name { get; } = "Redis"; + /// /// Saves the bucket information. /// diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs new file mode 100644 index 00000000..d71e8fc9 --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; +using System.Diagnostics.Metrics; + +namespace EventSourcing.Backbone.Channels.RedisProvider; + +internal class Telemetry +{ + public readonly static Meter Metrics = new(ProducerChannelConstants.REDIS_CHANNEL_SOURCE); + public static readonly ActivitySource Track = new ActivitySource(ProducerChannelConstants.REDIS_CHANNEL_SOURCE); +} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs index 235b4adc..327150ce 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs @@ -1,10 +1,12 @@ -using System.Threading; +using System.Diagnostics; +using System.Diagnostics.Metrics; using Microsoft.Extensions.Logging; using StackExchange.Redis; using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.Telemetry; #pragma warning disable S3881 // "IDisposable" should be implemented correctly #pragma warning disable S2953 // Methods named "Dispose" should implement "IDisposable.Dispose" @@ -28,6 +30,9 @@ public class EventSourceRedisConnectionFactory : IEventSourceRedisConnectionFact private readonly AsyncLock _lock = new AsyncLock(TimeSpan.FromSeconds(CLOSE_DELEY_MILLISECONDS)); private DateTime _lastResetConnection = DateTime.Now; private int _reconnectTry = 0; + private const string CHANGE_CONN = "redis-change-connection"; + private static readonly Counter ReConnectCounter = Metics.CreateCounter(CHANGE_CONN, "count", + "count how many time the connection was re-create"); #region Ctor @@ -175,9 +180,9 @@ async Task IEventSourceRedisConnectionFactory.GetAsync(C { _lastResetConnection = DateTime.Now; var cn = conn; -#pragma warning disable S1481 + Activity.Current?.AddEvent(CHANGE_CONN, t => t.Add("redis.operation-kind", Kind)); + ReConnectCounter.WithTag("redis.operation-kind", Kind).Add(1); Task _ = Task.Delay(CLOSE_DELEY_MILLISECONDS).ContinueWith(_ => cn.CloseAsync()); -#pragma warning restore S1481 _redisTask = _configuration.CreateProviderAsync(_logger); var newConn = await _redisTask; return newConn; diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs index 5ae8f16c..ba53fd1e 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs @@ -1,25 +1,27 @@ -namespace EventSourcing.Backbone.Channels.RedisProvider.Common +namespace EventSourcing.Backbone.Channels.RedisProvider.Common; + +public static class RedisChannelConstants { - public static class RedisChannelConstants - { - public const string CHANNEL_TYPE = "REDIS Channel V1"; - public const string META_ARRAY_SEPARATOR = "~|~"; + public const string CHANNEL_TYPE = "REDIS Channel V1"; + public const string REDIS_CHANNEL_SOURCE = "event-source-redis-channel"; + public const string META_ARRAY_SEPARATOR = "~|~"; - public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; - public const string PASSWORD_KEY = "REDIS_EVENT_SOURCE_PASS"; + public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; + public const string PASSWORD_KEY = "REDIS_EVENT_SOURCE_PASS"; - /// - /// a work around used to release messages back to the stream (consumer) - /// - public const string NONE_CONSUMER = "__NONE_CUNSUMER__"; + /// + /// a work around used to release messages back to the stream (consumer) + /// + public const string NONE_CONSUMER = "__NONE_CUNSUMER__"; - public static class MetaKeys - { - public const string SegmentsKeys = "segments-keys"; - public const string InterceptorsKeys = "interceptors-keys"; - public const string TelemetryBaggage = "telemetry-baggage"; - public const string TelemetrySpanId = "telemetry-span-id"; - public const string TelemetryTraceId = "telemetry-trace-id"; - } + public static class MetaKeys + { + public const string SegmentsKeys = "segments-keys"; + public const string InterceptorsKeys = "interceptors-keys"; + public const string TelemetryBaggage = "telemetry-baggage"; + public const string TelemetrySpanId = "telemetry-span-id"; + public const string TelemetryTraceId = "telemetry-trace-id"; } + } + diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 9ce38e49..a2f7a09c 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -1,7 +1,11 @@ -using Microsoft.Extensions.Logging; +using System.Diagnostics.Metrics; + +using Microsoft.Extensions.Logging; using StackExchange.Redis; +using static EventSourcing.Backbone.Channels.RedisProvider.Common.Telemetry; + namespace EventSourcing.Backbone.Private { /// @@ -9,8 +13,12 @@ namespace EventSourcing.Backbone.Private /// public static class RedisCommonProviderExtensions { - private const int MAX_DELAY = 15_000; - private const int KEY_NOT_EXISTS_DELAY = 3_000; + private const int MIN_DELAY = 2; + private const int SPIN_LIMIT = 30; + private const int MAX_DELAY = 3_000; + private static Counter KeyMissingCounter = Metics.CreateCounter("event-source.key-missing", "count", "count missing key events"); + private static Counter CreateConsumerGroupCounter = Metics.CreateCounter("event-source.create-consumer-group", "count", "creating a consumer group"); + private static Counter CreateConsumerGroupRetryCounter = Metics.CreateCounter("event-source.create-consumer-group-retry", "count", "retries of creating a consumer group"); private static readonly AsyncLock _lock = new AsyncLock(TimeSpan.FromSeconds(20)); @@ -33,52 +41,74 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( CancellationToken cancellationToken) { StreamGroupInfo[] groupsInfo = Array.Empty(); + using var track = Track.StartInternalTrace("event-source.consumer.create-consumer-group", t => t.Add("group-name", consumerGroup)); + CreateConsumerGroupCounter.Add(1); - int delay = 0; + int delay = MIN_DELAY; bool exists = false; int tryNumber = 0; while (groupsInfo.Length == 0) { + if (tryNumber != 0) + CreateConsumerGroupRetryCounter.Add(1); tryNumber++; IConnectionMultiplexer conn = await connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { - #region Validation (if key exists) - - if (!await db.KeyExistsAsync(eventSourceKey, - flags: CommandFlags.DemandMaster)) - { - await Task.Delay(KEY_NOT_EXISTS_DELAY); - logger.LogDebug("Key not exists (yet): {info}", CurrentInfo()); - continue; - } - - #endregion // Validation (if key exists) - #region delay on retry - if (delay == 0) - delay = 4; - else + if (tryNumber > SPIN_LIMIT) { delay = Math.Min(delay * 2, MAX_DELAY); - await Task.Delay(delay); + using (Track.StartInternalTrace("event-source.consumer.delay.key-not-exists", + t => t.Add("delay", delay) + .Add("try-number", tryNumber) + .Add("group-name", consumerGroup))) ; + { + await Task.Delay(delay); + } if (tryNumber % 10 == 0) { logger.LogWarning("Create Consumer Group If Not Exists: still waiting {info}", CurrentInfo()); } } - #endregion // delay on retry - using var lk = await _lock.AcquireAsync(cancellationToken); - groupsInfo = await db.StreamGroupInfoAsync( - eventSourceKey, - flags: CommandFlags.DemandMaster); - exists = groupsInfo.Any(m => m.Name == consumerGroup); + #region Validation (if key exists) + + if (!await db.KeyExistsAsync(eventSourceKey, + flags: CommandFlags.DemandMaster)) + { + KeyMissingCounter.Add(1); + //using (Track.StartInternalTrace("event-source.consumer.delay.key-not-exists", + // t => t.Add("delay", KEY_NOT_EXISTS_DELAY) + // .Add("try-number", tryNumber) + // .Add("group-name", consumerGroup))) ; + //{ + // // producer didn't produce anything yet + // await Task.Delay(KEY_NOT_EXISTS_DELAY); + //} + if (tryNumber == 0 || tryNumber > SPIN_LIMIT) + logger.LogDebug("Key not exists (yet): {info}", CurrentInfo()); + continue; + } + + #endregion // Validation (if key exists) + + using (Track.StartInternalTrace("event-source.consumer.get-consumer-group-info", + t => t + .Add("try-number", tryNumber) + .Add("group-name", consumerGroup))) ; + { + using var lk = await _lock.AcquireAsync(cancellationToken); + groupsInfo = await db.StreamGroupInfoAsync( + eventSourceKey, + flags: CommandFlags.DemandMaster); + exists = groupsInfo.Any(m => m.Name == consumerGroup); + } } #region Exception Handling @@ -91,7 +121,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( } else { - await Task.Delay(KEY_NOT_EXISTS_DELAY); + //await Task.Delay(KEY_NOT_EXISTS_DELAY); logger.LogDebug(ex, "Create Consumer Group If Not Exists: failed. {info}", CurrentInfo()); } } @@ -113,13 +143,19 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { try { - using var lk = await _lock.AcquireAsync(cancellationToken); - if (await db.StreamCreateConsumerGroupAsync(eventSourceKey, - consumerGroup, - StreamPosition.Beginning, - flags: CommandFlags.DemandMaster)) + using (Track.StartInternalTrace("event-source.consumer.create-consumer-group", + t => t + .Add("try-number", tryNumber) + .Add("group-name", consumerGroup))) ; { - break; + using var lk = await _lock.AcquireAsync(cancellationToken); + if (await db.StreamCreateConsumerGroupAsync(eventSourceKey, + consumerGroup, + StreamPosition.Beginning, + flags: CommandFlags.DemandMaster)) + { + break; + } } } #region Exception Handling diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs deleted file mode 100644 index a1046369..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisTelemetryExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Diagnostics; - -namespace EventSourcing.Backbone; - -public static class RedisTelemetryExtensions -{ - #region InjectTelemetryTags - - /// - /// Adds standard open-telemetry tags (for redis). - /// - /// The meta. - /// The activity. - public static void InjectTelemetryTags(this Metadata meta, Activity? activity) - { - // These tags are added demonstrating the semantic conventions of the OpenTelemetry messaging specification - // See: - // * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#messaging-attributes - activity?.SetTag("messaging.system", "redis-stream"); - activity?.SetTag("messaging.destination_kind", "topic"); - - activity?.SetTag("messaging.destination", meta.Operation); - activity?.SetTag("messaging.message_id", meta.MessageId); - activity?.SetTag("messaging.redis.key", meta.Uri); - - meta.InjectMetaTelemetryTags(activity); - } - - #endregion // InjectTelemetryTags -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs new file mode 100644 index 00000000..8cf8cd6b --- /dev/null +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs @@ -0,0 +1,11 @@ +using System.Diagnostics; +using System.Diagnostics.Metrics; + +namespace EventSourcing.Backbone.Channels.RedisProvider.Common; + + +internal class Telemetry +{ + public readonly static Meter Metics = new(RedisChannelConstants.REDIS_CHANNEL_SOURCE); + public static readonly ActivitySource Track = new ActivitySource(RedisChannelConstants.REDIS_CHANNEL_SOURCE); +} diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs index 427499ee..fda173c7 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreConsumerProvider/S3ConsumerStorageStrategy.cs @@ -51,6 +51,11 @@ public S3ConsumerStorageStrategy( #endregion // ctor + /// + /// Gets the name of the storage provider. + /// + public string Name { get; } = "S3"; + /// /// Load the bucket information. /// diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs index 4f300965..c2fc1530 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProducerProvider/S3ProducerStorageStrategy.cs @@ -53,6 +53,11 @@ public S3ProducerStorageStrategy( #endregion // ctor + /// + /// Gets the name of the storage provider. + /// + public string Name { get; } = "S3"; + /// /// Saves the bucket information. /// diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs index ee45896f..a914b706 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/FilteredStorageStrategy.cs @@ -25,6 +25,11 @@ public FilteredStorageStrategy( _targetType = targetType; } + /// + /// Gets the name of the storage provider. + /// + public string Name => _storage.Name; + /// /// Determines whether is of the right target type. /// diff --git a/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs b/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs new file mode 100644 index 00000000..ebf7f4d7 --- /dev/null +++ b/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs @@ -0,0 +1,41 @@ +using System.Collections; +using System.Diagnostics; + +namespace EventSourcing.Backbone.Consumers; + +public static class ConsumerTelemetryExtensions +{ + #region StartConsumerTrace + + /// + /// Starts a consumer trace. + /// + /// The activity source. + /// The metadata. + /// The parent context. + /// The tags action. + /// + public static Activity? StartConsumerTrace(this ActivitySource activitySource, + Metadata meta, + ActivityContext parentContext, + Action? tagsAction = null) + { + // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + var activityName = $"event-source.Consumer.{meta.Operation}.process"; + + var tags = new ActivityTagsCollection(); + var t = new TagAddition(tags); + tagsAction?.Invoke(t); + + Activity? activity = activitySource.StartActivity( + activityName, + ActivityKind.Consumer, + parentContext, tags, links: new ActivityLink(parentContext).ToEnumerable()); + meta.InjectTelemetryTags(activity); + + return activity; + } + + #endregion // StartConsumerTrace +} diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/Ack.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/Ack.cs index b2badabc..4e3c2392 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/Ack.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/Ack.cs @@ -57,13 +57,17 @@ public static IAsyncDisposable Set(IAck ack) /// Preform acknowledge (which should prevent the /// message from process again by the consumer) ///
+ /// The cause of the acknowledge. /// - public ValueTask AckAsync() => ValueTask.CompletedTask; + public ValueTask AckAsync(AckBehavior cause) => ValueTask.CompletedTask; /// /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) /// - public ValueTask CancelAsync() => ValueTask.CompletedTask; + /// The cause of the cancellation. + /// + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) + public ValueTask CancelAsync(AckBehavior cause) => ValueTask.CompletedTask; /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs index 43d44a47..184c08e1 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs @@ -9,9 +9,9 @@ namespace EventSourcing.Backbone /// public class AckOnce : IAck { - private static readonly Func NON_FN = () => ValueTask.CompletedTask; - private readonly Func _ackAsync; - private readonly Func _cancelAsync; + private static readonly Func NON_FN = (_) => ValueTask.CompletedTask; + private readonly Func _ackAsync; + private readonly Func _cancelAsync; private readonly AckBehavior _behavior; private readonly ILogger _logger; private int _ackCount = 0; @@ -26,10 +26,10 @@ public class AckOnce : IAck /// The behavior. /// The logger. public AckOnce( - Func ackAsync, + Func ackAsync, AckBehavior behavior, ILogger logger, - Func? cancelAsync = null) + Func? cancelAsync = null) { _ackAsync = ackAsync; _cancelAsync = cancelAsync ?? NON_FN; @@ -42,17 +42,18 @@ public AckOnce( #region AckAsync /// - /// Preform acknowledge (which should prevent the + /// Preform acknowledge (which should prevent the /// message from process again by the consumer) /// + /// The cause of the acknowledge. /// - public async ValueTask AckAsync() + public async ValueTask AckAsync(AckBehavior cause) { int count = Interlocked.Increment(ref _ackCount); try { if (count == 1) - await _ackAsync(); + await _ackAsync(cause); } catch (Exception ex) @@ -71,14 +72,16 @@ public async ValueTask AckAsync() /// /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) /// + /// The cause of the cancellation. /// - public async ValueTask CancelAsync() + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) + public async ValueTask CancelAsync(AckBehavior cause) { int count = Interlocked.Increment(ref _ackCount); try { if (count == 1) - await _cancelAsync(); + await _cancelAsync(cause); } catch (Exception ex) @@ -105,7 +108,7 @@ public async ValueTask DisposeAsync() { if (_behavior == AckBehavior.OnSucceed) { - await AckAsync(); + await AckAsync(AckBehavior.OnSucceed); } } diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs index d2e45a99..d2144895 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/IAckOperations.cs @@ -8,17 +8,20 @@ public interface IAckOperations { /// - /// Preform acknowledge (which should prevent the + /// Preform acknowledge (which should prevent the /// message from process again by the consumer). - /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) /// - ValueTask AckAsync(); + /// The cause of the acknowledge. + /// + ValueTask AckAsync(AckBehavior cause = AckBehavior.Manual); /// /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) /// /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing) + /// The cause of the cancellation. /// - ValueTask CancelAsync(); + ValueTask CancelAsync(AckBehavior cause = AckBehavior.Manual); } } diff --git a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs index dc470084..945fa6c2 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs @@ -76,11 +76,13 @@ public static implicit operator Metadata(ConsumerMetadata? instance) #region AckAsync /// - /// Preform acknowledge (which should prevent the + /// Preform acknowledge (which should prevent the /// message from process again by the consumer) /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing). /// - public ValueTask AckAsync() => Ack.Current.AckAsync(); + /// The cause of the acknowledge. + /// + public ValueTask AckAsync(AckBehavior cause = AckBehavior.Manual) => Ack.Current.AckAsync(cause); #endregion // AckAsync @@ -89,9 +91,10 @@ public static implicit operator Metadata(ConsumerMetadata? instance) /// /// Cancel acknowledge (will happen on error in order to avoid ack on succeed) /// - /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing). + /// The cause of the cancellation. /// - public ValueTask CancelAsync() => Ack.Current.AckAsync(); + /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing). + public ValueTask CancelAsync(AckBehavior cause = AckBehavior.Manual) => Ack.Current.AckAsync(cause); #endregion // AckAsync diff --git a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerOptions.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerOptions.cs index 1dba4a09..9d067b1b 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerOptions.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerOptions.cs @@ -122,16 +122,6 @@ public DateTimeOffset? FetchUntilDateOrEmpty #endregion MaxMessages - #region TraceAsParent - - /// - /// Gets the threshold duration which limit considering producer call as parent, - /// Beyond this period the consumer span will refer the producer as Link (rather than parent). - /// - public TimeSpan TraceAsParent { get; init; } - - #endregion // TraceAsParent - #region OriginFilter /// diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerStorageStrategy.cs b/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerStorageStrategy.cs index 041aba02..5fa178fb 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerStorageStrategy.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Provider/IConsumerStorageStrategy.cs @@ -9,6 +9,11 @@ /// public interface IConsumerStorageStrategy { + /// + /// Gets the name of the storage provider. + /// + string Name { get; } + /// /// Load the bucket information. /// diff --git a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs index 58cc8d13..6308f36e 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; namespace EventSourcing.Backbone; diff --git a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs b/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs deleted file mode 100644 index d286a5e5..00000000 --- a/EventSourcing.Backbone.Abstractions/Extensions/EventSourcingExtensions.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Diagnostics; - -using OpenTelemetry; -using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Trace; - -using static System.Diagnostics.TelemetryrExtensions; -namespace EventSourcing.Backbone; - -public static class EventSourcingExtensions -{ - ///// - ///// Adds the event consumer telemetry source (will result in tracing the consumer). - ///// - ///// The builder. - ///// - //public static TracerProviderBuilder ListenToEventSourceRedisChannel( - // this TracerProviderBuilder builder) => - // builder.AddSource( - // EventSourceConstants.Consumer.REDIS_CHANNEL_SOURCE, - // EventSourceConstants.Producer.REDIS_CHANNEL_SOURCE); - - #region ExtractSpan - - /// - /// Extract telemetry span's parent info - /// - /// - /// The meta. - /// The entries for extraction. - /// The injection strategy. - /// - public static ActivityContext ExtractSpan( - this Metadata meta, - T entries, - Func> injectStrategy) - { - PropagationContext parentContext = Propagator.Extract(default, entries, injectStrategy); - Baggage.Current = parentContext.Baggage; - - // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - return parentContext.ActivityContext; - } - - #endregion // ExtractSpan - - #region InjectMetaTelemetryTags - - /// - /// Adds standard open-telemetry tags (for redis). - /// - /// The meta. - /// The activity. - public static void InjectMetaTelemetryTags(this Metadata meta, Activity? activity) - { - activity?.SetTag("event-source.uri", meta.Uri); - activity?.SetTag("event-source.operation", meta.Operation); - activity?.SetTag("event-source.message-id", meta.MessageId); - activity?.SetTag("event-source.channel-type", meta.ChannelType); - } - - #endregion // InjectMetaTelemetryTags -} diff --git a/EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs b/EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs deleted file mode 100644 index 680ffe15..00000000 --- a/EventSourcing.Backbone.Abstractions/Extensions/TelemetryrExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Immutable; - -using EventSourcing.Backbone; - -using OpenTelemetry; -using OpenTelemetry.Context.Propagation; - -namespace System.Diagnostics; - -public static class TelemetryrExtensions -{ - internal static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; - - #region StartSpanScope - - /// - /// Inject telemetry span to the channel property. - /// - /// - /// The activity. - /// The meta. - /// The entries builder. - /// The injection strategy. - /// - public static void InjectSpan( - this Activity? activity, - Metadata meta, - ImmutableArray.Builder entriesBuilder, - Action.Builder, string, string> injectStrategy) - { - // Depending on Sampling (and whether a listener is registered or not), the - // activity above may not be created. - // If it is created, then propagate its context. - // If it is not created, the propagate the Current context, - // if any. - if (activity != null) - { - Activity.Current = activity; - } - ActivityContext contextToInject = Activity.Current?.Context ?? default; - - // Inject the ActivityContext into the message metadata to propagate trace context to the receiving service. - Propagator.Inject(new PropagationContext(contextToInject, Baggage.Current), entriesBuilder, injectStrategy); - } - - #endregion // StartSpanScope -} diff --git a/EventSourcing.Backbone.Abstractions/Producer/ProducerExtensions.cs b/EventSourcing.Backbone.Abstractions/Producer/ProducerExtensions.cs index 2077b286..b2f8dbbd 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/ProducerExtensions.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/ProducerExtensions.cs @@ -56,6 +56,12 @@ public VoidStorageStrategy(string providerPrefix) #endregion // Ctor + /// + /// Gets the name of the storage provider. + /// + public string Name { get; } = "void-storage"; + + /// /// Saves the bucket information. /// diff --git a/EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerStorageStrategy.cs b/EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerStorageStrategy.cs index 02207790..e315973e 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerStorageStrategy.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Provider/IProducerStorageStrategy.cs @@ -11,6 +11,11 @@ namespace EventSourcing.Backbone /// public interface IProducerStorageStrategy { + /// + /// Gets the name of the storage provider. + /// + string Name { get; } + /// /// Saves the bucket information. /// diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs b/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs new file mode 100644 index 00000000..11380b4d --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs @@ -0,0 +1,152 @@ +using System.Collections; +using System.Collections.Immutable; +using System.Diagnostics; + +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; + +namespace EventSourcing.Backbone; + +public static class EventSourceTelemetryExtensions +{ + internal static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; + + #region InjectSpan + + /// + /// Inject telemetry span to the channel property (Before sending). + /// + /// + /// The activity. + /// The entries builder. + /// The injection strategy. + /// + public static void InjectSpan( + this Activity? activity, + ImmutableArray.Builder entriesBuilder, + Action.Builder, string, string> injectStrategy) + { + // Depending on Sampling (and whether a listener is registered or not), the + // activity above may not be created. + // If it is created, then propagate its context. + // If it is not created, the propagate the Current context, + // if any. + if (activity != null) + { + Activity.Current = activity; + } + ActivityContext contextToInject = Activity.Current?.Context ?? default; + + // Inject the ActivityContext + Propagator.Inject(new PropagationContext(contextToInject, Baggage.Current), entriesBuilder, injectStrategy); + } + + #endregion // InjectSpan + + #region ExtractSpan + + /// + /// Extract telemetry span's parent info (while consuming) + /// + /// + /// The entries for extraction. + /// The injection strategy. + /// + public static ActivityContext ExtractSpan( + T entries, + Func> injectStrategy) + { + PropagationContext parentContext = Propagator.Extract(default, entries, injectStrategy); + Baggage.Current = parentContext.Baggage; + + // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + return parentContext.ActivityContext; + } + + #endregion // ExtractSpan + + #region InjectTelemetryTags + + /// + /// Adds standard open-telemetry tags (for redis). + /// + /// The meta. + /// The activity. + public static void InjectTelemetryTags(this Metadata meta, Activity? activity) + { + // These tags are added demonstrating the semantic conventions of the OpenTelemetry messaging specification + // See: + // * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#messaging-attributes + + activity?.SetTag("event-source.uri", meta.Uri); + activity?.SetTag("event-source.operation", meta.Operation); + activity?.SetTag("event-source.message-id", meta.MessageId); + activity?.SetTag("event-source.channel-type", meta.ChannelType); + } + + #endregion // InjectTelemetryTags + + #region StartInternalTrace + + /// + /// Starts a trace. + /// + /// The activity source. + /// The name. + /// The tags action. + /// + public static Activity? StartInternalTrace(this ActivitySource activitySource, + string name, + Action? tagsAction = null) + { + // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + + var tags = new ActivityTagsCollection(); + var t = new TagAddition(tags); + tagsAction?.Invoke(t); + + ActivityContext? context = Activity.Current?.Context; + Activity? activity; + if (context == null) + { + activity = activitySource.StartActivity( + name, + ActivityKind.Internal, + Activity.Current?.Context ?? default, tags); + } + else + { + var link = new ActivityLink((ActivityContext)context); + activity = activitySource.StartActivity( + name, + ActivityKind.Internal, + Activity.Current?.Context ?? default, tags, link.ToEnumerable()); + } + + return activity; + } + + #endregion // StartInternalTrace + + #region AddEvent + + /// + /// Adds the event. + /// + /// The current. + /// The name. + /// The tags action. + /// + public static Activity? AddEvent(this Activity current, string name, Action? tagsAction = null) + { + var tags = new ActivityTagsCollection(); + var t = new TagAddition(tags); + tagsAction?.Invoke(t); + var e = new ActivityEvent(name, tags: tags); + return current?.AddEvent(e); + } + + #endregion // AddEvent +} diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/ITagAddition.cs b/EventSourcing.Backbone.Abstractions/Telemetry/ITagAddition.cs new file mode 100644 index 00000000..074ce88e --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Telemetry/ITagAddition.cs @@ -0,0 +1,6 @@ +namespace EventSourcing.Backbone; + +public interface ITagAddition +{ + ITagAddition Add(string key, T value); +} diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/TagAddition.cs b/EventSourcing.Backbone.Abstractions/Telemetry/TagAddition.cs new file mode 100644 index 00000000..848a7435 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Telemetry/TagAddition.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; + +namespace EventSourcing.Backbone; + +/// +/// Tag addition +/// +/// +public class TagAddition : ITagAddition +{ + private readonly ActivityTagsCollection _tags; + + public TagAddition(ActivityTagsCollection tags) + { + _tags = tags; + } + public ITagAddition Add(string key, T value) + { + _tags.Add(key, value); + return this; + } +} diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/deprecated/ITelemetryAbstraction.cs b/EventSourcing.Backbone.Abstractions/Telemetry/deprecated/ITelemetryAbstraction.cs new file mode 100644 index 00000000..7f12f4a4 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Telemetry/deprecated/ITelemetryAbstraction.cs @@ -0,0 +1,7 @@ + +///// +///// Telemetry api +///// +//public interface ITelemetryAbstraction +//{ +//} diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/deprecated/TelemetryAbstraction.cs b/EventSourcing.Backbone.Abstractions/Telemetry/deprecated/TelemetryAbstraction.cs new file mode 100644 index 00000000..cb362cac --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Telemetry/deprecated/TelemetryAbstraction.cs @@ -0,0 +1,91 @@ +//using System.Diagnostics; +//using System.Runtime.CompilerServices; +//using OpenTelemetry.Context.Propagation; + +//using OpenTelemetry; + +//namespace EventSourcing.Backbone.Telemetry; + +///// +///// Telemetry api +///// +//public class TelemetryAbstraction: ITelemetryAbstraction +//{ +// internal static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; + +// /// +// /// Telemetry operators +// /// +// private readonly ActivitySource _telemetry; + +// public TelemetryAbstraction(string source) +// { +// _telemetry = new ActivitySource(source); +// } + +// /// +// /// Starts a trace. +// /// +// /// The name. +// /// The kind. +// /// +// private Activity? Start([CallerMemberName] string name = "", ActivityKind kind = ActivityKind.Internal) +// { +// return _telemetry.StartActivity(name, kind); +// } + +// /// +// /// Starts a trace. +// /// +// /// The name. +// /// The kind. +// /// The parent context. +// /// The tags. +// /// The links. +// /// The start time. +// /// +// private Activity? Start( +// string name, +// ActivityKind kind, +// ActivityContext parentContext, +// IEnumerable>? tags = null, +// IEnumerable? links = null, +// DateTimeOffset startTime = default) +// { +// return _telemetry.StartActivity(name, kind, parentContext, tags, links, startTime); +// } + +// /// +// /// Starts a trace. +// /// +// /// The name. +// /// The kind. +// /// The parent identifier. +// /// The tags. +// /// The links. +// /// The start time. +// /// +// private Activity? Start( +// string name, +// ActivityKind kind, +// string? parentId, +// IEnumerable>? tags = null, +// IEnumerable? links = null, +// DateTimeOffset startTime = default) + +// { +// return _telemetry.StartActivity(name, kind, parentId, tags, links, startTime); +// } + +// private Activity? Start( +// ActivityKind kind, +// ActivityContext parentContext = default, +// IEnumerable>? tags = null, +// IEnumerable? links = null, +// DateTimeOffset startTime = default, +// [CallerMemberName] string name = "") + +// { +// return _telemetry.StartActivity(kind, parentContext, tags, links, startTime, name); +// } +//} diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index c5a297be..79b634da 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -12,6 +12,7 @@ + diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index 49a00b7d..b6cf688b 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -1,5 +1,6 @@ using EventSourcing.Backbone; using EventSourcing.Backbone.Channels; +using EventSourcing.Backbone.Channels.RedisProvider.Common; using Microsoft.Extensions.Hosting; @@ -62,7 +63,8 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( { var sources = new[] { (string)env, ProducerChannelConstants.REDIS_CHANNEL_SOURCE, - ConsumerChannelConstants.REDIS_CHANNEL_SOURCE}; + ConsumerChannelConstants.REDIS_CHANNEL_SOURCE, + RedisChannelConstants.REDIS_CHANNEL_SOURCE }; tracerProviderBuilder .AddSource(sources) @@ -113,7 +115,12 @@ public static OpenTelemetryBuilder WithEventSourcingMetrics( builder.WithMetrics(metricsProviderBuilder => { metricsProviderBuilder - .ConfigureResource(resource => resource.AddService(env)); + .ConfigureResource(resource => resource.AddService(env) + .AddService(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE)) + .AddMeter(env, + ProducerChannelConstants.REDIS_CHANNEL_SOURCE, + ConsumerChannelConstants.REDIS_CHANNEL_SOURCE, + RedisChannelConstants.REDIS_CHANNEL_SOURCE); injection?.Invoke(metricsProviderBuilder); }); diff --git a/Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs b/Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs index 62d096f7..c9a87c8c 100644 --- a/Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers/Builder/FilteredStorageStrategy.cs @@ -12,7 +12,6 @@ internal class FilteredStorageStrategy : IProducerStorageStrategyWithFilter private readonly IProducerStorageStrategy _storage; private readonly Predicate? _filter; private readonly EventBucketCategories _targetType; - private readonly static (string key, string metadata)[] EMPTY_RESULT = Array.Empty<(string key, string metadata)>(); /// /// Initializes a new instance. @@ -30,6 +29,11 @@ public FilteredStorageStrategy( _targetType = targetType; } + /// + /// Gets the name of the storage provider. + /// + public string Name => _storage.Name; + /// /// Determines whether is of the right target type. /// diff --git a/Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs b/Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs index 2be0d971..3c66589f 100644 --- a/Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs +++ b/Producers/EventSourcing.Backbone.Producers/ProducerDefaultSegmentationStrategy.cs @@ -1,20 +1,19 @@ -namespace EventSourcing.Backbone -{ +namespace EventSourcing.Backbone; + - public class ProducerDefaultSegmentationStrategy : - IProducerAsyncSegmentationStrategy +public class ProducerDefaultSegmentationStrategy : + IProducerAsyncSegmentationStrategy +{ + ValueTask IProducerAsyncSegmentationStrategy. + TryClassifyAsync( + Bucket segments, + string operation, + string argumentName, + T producedData, + EventSourceOptions options) { - ValueTask IProducerAsyncSegmentationStrategy. - TryClassifyAsync( - Bucket segments, - string operation, - string argumentName, - T producedData, - EventSourceOptions options) - { - ReadOnlyMemory data = options.Serializer.Serialize(producedData); - string key = argumentName; - return segments.Add(key, data).ToValueTask(); - } + ReadOnlyMemory data = options.Serializer.Serialize(producedData); + string key = argumentName; + return segments.Add(key, data).ToValueTask(); } } diff --git a/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs b/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs new file mode 100644 index 00000000..243df6e6 --- /dev/null +++ b/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; + +namespace EventSourcing.Backbone.Producers; + +public static class ProducerTelemetryExtensions +{ + #region StartProducerTrace + + /// + /// Starts a consumer trace. + /// + /// The activity source. + /// The metadata. + /// The tags action. + /// + public static Activity? StartProducerTrace(this ActivitySource activitySource, + Metadata meta, + Action? tagsAction = null) + { + // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + + var tags = new ActivityTagsCollection(); + var t = new TagAddition(tags); + tagsAction?.Invoke(t); + + var activityName = $"event-source.producer.{meta.Operation}.send"; + Activity? activity = activitySource.StartActivity(activityName, ActivityKind.Producer); + meta.InjectTelemetryTags(activity); + + return activity; + } + + #endregion // StartProducerTrace +} diff --git a/Tests/ConsoleTest/Constants.cs b/Tests/ConsoleTest/Constants.cs index 8fd2f8a9..7b0907a2 100644 --- a/Tests/ConsoleTest/Constants.cs +++ b/Tests/ConsoleTest/Constants.cs @@ -4,7 +4,7 @@ namespace ConsoleTest; internal static class Constants { - public const int MAX = 3_000; + public const int MAX = 100; //3_000; public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; public const string ENV = $"console-test"; public static readonly IFooConsumer Subscriber = A.Fake(); diff --git a/Tests/ConsoleTest/IFoo.cs b/Tests/ConsoleTest/IFoo.cs index 92a7b830..57326a79 100644 --- a/Tests/ConsoleTest/IFoo.cs +++ b/Tests/ConsoleTest/IFoo.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using EventSourcing.Backbone; +using EventSourcing.Backbone; namespace ConsoleTest; diff --git a/Tests/ConsoleTest/OpenTelemetryExtensions.cs b/Tests/ConsoleTest/OpenTelemetryExtensions.cs index 5474d328..0f653da8 100644 --- a/Tests/ConsoleTest/OpenTelemetryExtensions.cs +++ b/Tests/ConsoleTest/OpenTelemetryExtensions.cs @@ -41,7 +41,7 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this IServiceColl }) .WithEventSourcingMetrics(environment, cfg => { - cfg + cfg .AddOtlpExporter() .AddPrometheusExporter() .AddMeter("ConsoleTest"); diff --git a/Tests/ConsoleTest/Program.cs b/Tests/ConsoleTest/Program.cs index 1a6b759e..1df0c02d 100644 --- a/Tests/ConsoleTest/Program.cs +++ b/Tests/ConsoleTest/Program.cs @@ -1,21 +1,21 @@ #pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation #pragma warning disable HAA0301 // Closure Allocation Source -using EventSourcing.Backbone; -using System; using System.Diagnostics; -using System.Text.Json; -using EventSourcing.Backbone.Building; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Configuration; -using StackExchange.Redis; +using System.Threading.Tasks.Dataflow; using ConsoleTest; -using FakeItEasy; -using System.Threading.Tasks.Dataflow; + +using EventSourcing.Backbone; using EventSourcing.Backbone.Enums; -using Microsoft.Extensions.Hosting; + +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using StackExchange.Redis; + using static ConsoleTest.Constants; CancellationTokenSource cancellation = new CancellationTokenSource( @@ -67,6 +67,9 @@ await hosing.RunAsync(cancellation.Token); +Console.WriteLine("Done!"); +Console.ReadKey(false); + static async Task Cleanup(string END_POINT_KEY, string URI, ILogger fakeLogger) { IConnectionMultiplexer conn = RedisClientFactory.CreateProviderAsync( diff --git a/Tests/ConsoleTest/Worker.cs b/Tests/ConsoleTest/Worker.cs index f5fa14a6..0a523763 100644 --- a/Tests/ConsoleTest/Worker.cs +++ b/Tests/ConsoleTest/Worker.cs @@ -5,16 +5,9 @@ using FakeItEasy; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - -using StackExchange.Redis; - using static ConsoleTest.Constants; // see: diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs index bb3b14c6..cd6e461f 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider; @@ -12,13 +11,9 @@ using Polly; -using StackExchange.Redis; - using Xunit; using Xunit.Abstractions; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; - #pragma warning disable S3881 // "IDisposable" should be implemented correctly #pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index a87ae54c..57e905f2 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider; @@ -9,13 +8,9 @@ using Microsoft.Extensions.Logging; -using StackExchange.Redis; - using Xunit; using Xunit.Abstractions; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; - #pragma warning disable S3881 // "IDisposable" should be implemented correctly // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest @@ -31,9 +26,6 @@ public class EndToEndStressTests : TestsBase private readonly string ENV = $"test"; protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly ILogger _fakeLogger = A.Fake(); - private const int TIMEOUT = 1000 * 30; - #region Ctor public EndToEndStressTests( diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 1c2d487b..07a7a49f 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Text.Json; -using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider; @@ -13,12 +12,9 @@ using Polly; -using StackExchange.Redis; - using Xunit; using Xunit.Abstractions; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; using static EventSourcing.Backbone.EventSourceConstants; #pragma warning disable S3881 // "IDisposable" should be implemented correctly @@ -48,9 +44,7 @@ public class EndToEndTests : TestsBase private readonly string ENV = $"test"; protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; - private const int TIMEOUT = 1_000 * 50; #region Ctor diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index cd001fad..8e3c7898 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Diagnostics; -using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider; @@ -13,13 +12,9 @@ using Microsoft.Extensions.Logging; -using StackExchange.Redis; - using Xunit; using Xunit.Abstractions; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; - // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest namespace EventSourcing.Backbone.Tests @@ -39,8 +34,6 @@ public class InheritanceTests : TestsBase private readonly string ENV = $"test"; protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly ILogger _fakeLogger = A.Fake(); - private const int TIMEOUT = 1_000 * 50; #region Ctor @@ -54,7 +47,7 @@ public InheritanceTests( ITestOutputHelper outputHelper, Func? producerChannelBuilder = null, Func? consumerChannelBuilder = null) - :base(outputHelper) + : base(outputHelper) { _producerBuilder = ProducerBuilder.Empty.UseRedisChannel( /*, configuration: (cfg) => cfg.ServiceName = "mymaster" */); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 105be70d..8debe44e 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Threading.Tasks.Dataflow; using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider; @@ -10,13 +9,9 @@ using Microsoft.Extensions.Logging; -using StackExchange.Redis; - using Xunit; using Xunit.Abstractions; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; - // docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest namespace EventSourcing.Backbone.Tests @@ -33,9 +28,7 @@ public class MigrationTest : TestsBase private readonly string ENV = $"Development"; protected override string URI { get; } = $"{DateTime.UtcNow:yyyy-MM-dd HH_mm_ss}:{Guid.NewGuid():N}"; - private readonly ILogger _fakeLogger = A.Fake(); private static readonly User USER = new User { Eracure = new Personal { Name = "mike", GovernmentId = "A25" }, Comment = "Do it" }; - private const int TIMEOUT = 1_000 * 30; #region Ctor diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index 6082ef8a..f5c43951 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -5,10 +5,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; -using OpenTelemetry.Exporter; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - using StackExchange.Redis; // Configuration: https://medium.com/@gparlakov/the-confusion-of-asp-net-configuration-with-environment-variables-c06c545ef732 diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs index e0d58b3c..d9605b5e 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Controllers/EventSourceApiController.cs @@ -3,8 +3,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using StackExchange.Redis; - namespace EventSourcing.Backbone.WebEventTest.Controllers { [Route("api/[controller]")] diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index 22461154..f7983efa 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -24,6 +24,10 @@ + + + + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs index 6d275050..2295c943 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs @@ -31,12 +31,23 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati .WithEventSourcingTracing(environment, cfg => { - cfg.AddAspNetCoreInstrumentation(m => - { - m.Filter = TraceFilter; - m.RecordException = true; - m.EnableGrpcAspNetCoreSupport = true; - }) + cfg + //.AddSource("StackExchange.Redis") + //.AddSource("StackExchangeRedisConnectionInstrumentation") + //.AddRedisInstrumentation(RedisClientFactory.CreateProviderAsync().Result, c => + //{ + // c.FlushInterval = TimeSpan.FromSeconds(1); + //}) + //.ConfigureRedisInstrumentation((provider, b) => { + // var conn = provider.GetRequiredService(); + // b.AddConnection(conn); + //}) + .AddAspNetCoreInstrumentation(m => + { + m.Filter = TraceFilter; + m.RecordException = true; + m.EnableGrpcAspNetCoreSupport = true; + }) .AddHttpClientInstrumentation(m => { // m.Enrich @@ -72,9 +83,9 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati "/health" => false, "/readiness" => false, "/metrics" => false, - string x when x.StartsWith("/swagger") => false, - string x when x.StartsWith("/_framework/") => false, - string x when x.StartsWith("/_vs/") => false, + string x when x.StartsWith("/swagger") => false, + string x when x.StartsWith("/_framework/") => false, + string x when x.StartsWith("/_vs/") => false, _ => true }; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs index 4abfacbf..9f80479a 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/ProductCycle/Extensions/ConsumerExtensions.cs @@ -30,7 +30,6 @@ public static IServiceCollection AddConsumer .ResolveS3Storage(s3Options) .WithOptions(o => o with { - TraceAsParent = TimeSpan.FromMinutes(10), OriginFilter = MessageOrigin.Original, AckBehavior = AckBehavior.OnSucceed }) diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 2c624cac..9aa4a187 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -17,12 +17,12 @@ IWebHostEnvironment environment = builder.Environment; string env = environment.EnvironmentName; string appName = environment.ApplicationName; -string shortAppName = appName.Replace("EventSourcing.Backbone.", string.Empty); var services = builder.Services; // Add services to the container. + services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle services.AddEndpointsApiExplorer(); @@ -78,6 +78,8 @@ services.AddOptions(); // enable usage of IOptionsSnapshot dependency injection +//var redis = await RedisClientFactory.CreateProviderAsync(); +//services.AddSingleton(redis); services.AddEventSourceRedisConnection(); services.AddConsumer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); services.AddProductCycleProducer(EventSourcingConstants.URI, EventSourcingConstants.S3_BUCKET, env); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs index 3d111c8b..2765bbe4 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/RegisterEventSourceExtensions.cs @@ -45,7 +45,6 @@ public static IServiceCollection AddEventSource( // .AddS3Storage(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { - TraceAsParent = TimeSpan.FromMinutes(10), OriginFilter = MessageOrigin.Original }) .Environment(env) @@ -60,7 +59,6 @@ public static IServiceCollection AddEventSource( // .AddS3Storage(new S3Options { EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }) .WithOptions(o => o with { - TraceAsParent = TimeSpan.FromMinutes(10), OriginFilter = MessageOrigin.Original }); return consumer; diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs index 891cdae1..111be172 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Extensions/ConsumerExtensions.cs @@ -72,7 +72,6 @@ private static IConsumerReadyBuilder BuildConsumer(string uri return ioc.ResolveRedisConsumerChannel() .WithOptions(o => o with { - TraceAsParent = TimeSpan.FromMinutes(10), OriginFilter = MessageOrigin.Original, AckBehavior = AckBehavior.OnSucceed }) diff --git a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs index 292568a4..1bc36415 100644 --- a/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs +++ b/Tests/WebSampleS3/Extensions/ConsumerExtensions.cs @@ -35,7 +35,6 @@ public static IServiceCollection AddShipmentTrackingConsumer .ResolveS3Storage(s3Options) .WithOptions(o => o with { - TraceAsParent = TimeSpan.FromMinutes(10), OriginFilter = MessageOrigin.Original }) .Environment(env) diff --git a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs index 41e39697..78a956d0 100644 --- a/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs +++ b/Tests/WebSampleS3/Extensions/OpenTelemetryExtensions.cs @@ -1,4 +1,6 @@ -using OpenTelemetry.Metrics; +using EventSourcing.Backbone; + +using OpenTelemetry.Metrics; using OpenTelemetry.Trace; // see: @@ -31,12 +33,15 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati .WithEventSourcingTracing(environment, cfg => { - cfg.AddAspNetCoreInstrumentation(m => - { - m.Filter = TraceFilter; - m.RecordException = true; - m.EnableGrpcAspNetCoreSupport = true; - }) + cfg.AddRedisInstrumentation( + RedisClientFactory.CreateProviderAsync().Result, + options => options.SetVerboseDatabaseStatements = true) + .AddAspNetCoreInstrumentation(m => + { + m.Filter = TraceFilter; + m.RecordException = true; + m.EnableGrpcAspNetCoreSupport = true; + }) .AddHttpClientInstrumentation(m => { // m.Enrich diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index 4882dd76..b4d4169e 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -40,6 +40,10 @@ + + + + From bc2e6e067ebd48d55bcb810cfde8a8d55cc2c813 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 22 Jun 2023 12:59:44 +0300 Subject: [PATCH 172/178] feat: telemetry --- .../RedisConsumerChannel.cs | 87 +++++++++++-------- .../Telemetry.cs | 1 - .../RedisCommonProviderExtensions.cs | 36 ++++---- ...one.Channels.S3StoreProvider.Common.csproj | 2 +- .../ConsumerBase.EventSourceSubscriber.cs | 4 +- .../Consumer/Ack/AckOnce.cs | 31 +++++-- .../Consumer/ConsumerMetadata.cs | 2 +- .../EventSourceConstants.cs | 3 + ...EventSourcing.Backbone.Abstractions.csproj | 2 +- .../JsonDataSerializer.cs | 43 +++++---- .../Telemetry.cs | 13 +++ .../EventSourceTelemetryExtensions.cs | 1 + .../EventSourcingOtel.cs | 12 +-- .../Program.cs | 2 + Tests/WebSampleS3/WebSampleS3.csproj | 2 +- 15 files changed, 143 insertions(+), 98 deletions(-) create mode 100644 EventSourcing.Backbone.Abstractions/Telemetry.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index e409f380..c8611d7b 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -26,15 +26,16 @@ namespace EventSourcing.Backbone.Channels.RedisProvider; /// internal class RedisConsumerChannel : IConsumerChannelProvider { - private static readonly Counter StealAmountCounter = Metrics.CreateCounter("event-source.consumer.message-stealing", "sum"); + private static readonly Counter StealCountCounter = Metrics.CreateCounter("event-source.consumer.events-stealing", "count", + "Attempt to get stale events (messages) from other consumer (which assumed malfunction)"); + private static readonly Counter StealAmountCounter = Metrics.CreateCounter("event-source.consumer.events-stealing.messages", "count", + "Attempt to get stale events (messages) from other consumer (which assumed malfunction)"); private static readonly Counter ConcumeBatchCountCounter = Metrics.CreateCounter("event-source.consumer.batch", "count", - "count of the number of consuming batches form the stream provider"); - private static readonly Counter ConcumeBatchSumCounter = Metrics.CreateCounter("event-source.consumer.batch", "sum", - "count of the messages consuming before process"); + "count of the number of non empty consuming batches form the stream provider"); + private static readonly Counter ConcumeBatchSumCounter = Metrics.CreateCounter("event-source.consumer.batch.events", "count", + "Sum of total consuming events (messages) before process"); private static readonly Counter ConcumeBatchFailureCounter = Metrics.CreateCounter("event-source.consumer.batch.failure", "count", "batch reading failure"); - private static readonly Counter AckCounter = Metrics.CreateCounter("event-source.consumer.ack", "count", - "Acknowledge count"); private const string BEGIN_OF_STREAM = "0000000000000"; /// @@ -218,7 +219,7 @@ private async Task SubsribeToSingleAsync( var claimingTrigger = options.ClaimingTrigger; var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - string key = plan.FullUri(); + string uri = plan.FullUri(); bool isFirstBatchOrFailure = true; CommandFlags flags = CommandFlags.None; @@ -229,13 +230,13 @@ private async Task SubsribeToSingleAsync( #region await db.CreateConsumerGroupIfNotExistsAsync(...) await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, + uri, RedisChannelConstants.NONE_CONSUMER, logger, cancellationToken); await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, + uri, plan.ConsumerGroup, logger, cancellationToken); @@ -381,16 +382,16 @@ IEnumerable ExtractTraceContext(Dictionary entri int local = i; var cancellableIds = results[local..].Select(m => m.Id); var ack = new AckOnce( + uri, async (cause) => { - Activity.Current?.AddEvent("event-source.consumer.event.ack", t => t.Add("cause", cause)); + Activity.Current?.AddEvent("event-source.consumer.event.ack", t => t.Add("cause", cause).Add("URI", uri)); await AckAsync(result.Id); }, plan.Options.AckBehavior, logger, async (cause) => { - AckCounter.Add(1); - Activity.Current?.AddEvent("event-source.consumer.event.cancel", t => t.Add("cause", cause)); + Activity.Current?.AddEvent("event-source.consumer.event.cancel", t => t.Add("cause", cause).Add("URI", uri)); batchCancellation.CancelSafe(); // cancel forward await CancelAsync(cancellableIds); }); @@ -482,15 +483,18 @@ async Task ReadBatchAsync() { isFirstBatchOrFailure = false; string group = plan.ConsumerGroup; - using var activity = Track.StartInternalTrace("event-source.consumer.read-batch", t => t.Add("consumer-group", group)); + using var activity = Track.StartInternalTrace("event-source.consumer.read-batch", + t => t + .Add("consumer-group", group) + .Add("URI", uri) + ); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { - ConcumeBatchCountCounter.Add(1); values = await db.StreamReadGroupAsync( - key, + uri, group, plan.ConsumerName, position: StreamPosition.NewMessages, @@ -498,26 +502,27 @@ async Task ReadBatchAsync() flags: flags) .WithCancellation(ct, () => Array.Empty()) .WithCancellation(cancellationToken, () => Array.Empty()); + ConcumeBatchCountCounter.WithTag("URI", uri).Add(1); activity?.SetTag("count", values.Length); - ConcumeBatchSumCounter.Add(values.Length); + ConcumeBatchSumCounter.WithTag("URI", uri).Add(values.Length); } #region Exception Handling catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { - ConcumeBatchFailureCounter.Add(1); + ConcumeBatchFailureCounter.WithTag("URI", uri).Add(1); logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, + uri, plan.ConsumerGroup, logger, cancellationToken); } catch (RedisServerException ex) { - ConcumeBatchFailureCounter.Add(1); + ConcumeBatchFailureCounter.WithTag("URI", uri).Add(1); logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, + uri, plan.ConsumerGroup, logger, cancellationToken); } @@ -533,12 +538,12 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( catch (RedisTimeoutException ex) { - logger.LogWarning(ex, "Event source [{source}] by [{consumer}]: Timeout", key, plan.ConsumerName); + logger.LogWarning(ex, "Event source [{source}] by [{consumer}]: Timeout", uri, plan.ConsumerName); return Array.Empty(); } catch (Exception ex) { - logger.LogError(ex, "Fail to read from event source [{source}] by [{consumer}]", key, plan.ConsumerName); + logger.LogError(ex, "Fail to read from event source [{source}] by [{consumer}]", uri, plan.ConsumerName); return Array.Empty(); } @@ -564,7 +569,7 @@ async Task ReadSelfPending() try { StreamPendingMessageInfo[] pendMsgInfo = await db.StreamPendingMessagesAsync( - key, + uri, plan.ConsumerGroup, options.BatchSize, plan.ConsumerName, @@ -575,7 +580,7 @@ async Task ReadSelfPending() .Select(m => m.MessageId).ToArray(); if (ids.Length != 0) { - values = await db.StreamClaimAsync(key, + values = await db.StreamClaimAsync(uri, plan.ConsumerGroup, plan.ConsumerName, 0, @@ -593,7 +598,7 @@ async Task ReadSelfPending() catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, + uri, plan.ConsumerGroup, logger, cancellationToken); return Array.Empty(); @@ -617,6 +622,7 @@ async Task ClaimStaleMessages( if (values.Length != 0) return values; if (emptyBatchCount < claimingTrigger.EmptyBatchCount) return values; + string uri = plan.FullUri(); using var _ = Track.StartInternalTrace("event-source.consumer.stale-events"); try { @@ -624,8 +630,9 @@ async Task ClaimStaleMessages( StreamPendingInfo pendingInfo; using (Track.StartInternalTrace("event-source.consumer.events-stealing.pending")) { - pendingInfo = await db.StreamPendingAsync(key, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); + pendingInfo = await db.StreamPendingAsync(uri, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); } + StealCountCounter.WithTag("URI", uri).Add(1); foreach (var c in pendingInfo.Consumers) { var self = c.Name == plan.ConsumerName; @@ -634,10 +641,11 @@ async Task ClaimStaleMessages( { StreamPendingMessageInfo[] pendMsgInfo; using (Track.StartInternalTrace("event-source.consumer.events-stealing.pending-events", - t => t.Add("from-consumer", c.Name))) + t => t.Add("from-consumer", c.Name) + .Add("URI", uri))) { pendMsgInfo = await db.StreamPendingMessagesAsync( - key, + uri, plan.ConsumerGroup, 10, c.Name, @@ -658,13 +666,14 @@ async Task ClaimStaleMessages( #endregion // Log int count = ids.Length; - StealAmountCounter.WithTag("from-consumer", c.Name).Add(count); + StealAmountCounter.WithTag("from-consumer", c.Name).WithTag("URI", uri).Add(count); // will claim events only if older than _setting.ClaimingTrigger.MinIdleTime using (Track.StartInternalTrace("event-source.consumer.events-stealing.claim", t => t.Add("from-consumer", c.Name) - .Add("message-count", count))) + .Add("message-count", count) + .Add("URI", uri))) { - values = await db.StreamClaimAsync(key, + values = await db.StreamClaimAsync(uri, plan.ConsumerGroup, c.Name, minIdleTime, @@ -722,7 +731,7 @@ async ValueTask AckAsync(RedisValue messageId) IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); // release the event (won't handle again in the future) - await db.StreamAcknowledgeAsync(key, + await db.StreamAcknowledgeAsync(uri, plan.ConsumerGroup, messageId, flags: CommandFlags.DemandMaster); @@ -773,7 +782,10 @@ async Task ReleaseAsync(RedisValue[] freeTargets) IDatabaseAsync db = conn.GetDatabase(); try { - using (Track.StartInternalTrace("event-source.consumer.release-ownership", t => t.Add("consumer-group", plan.ConsumerGroup))) + using (Track.StartInternalTrace("event-source.consumer.release-ownership", + t => t + .Add("consumer-group", plan.ConsumerGroup) + .Add("URI", uri))) { await db.StreamClaimAsync(plan.FullUri(), plan.ConsumerGroup, @@ -782,7 +794,9 @@ await db.StreamClaimAsync(plan.FullUri(), freeTargets, flags: CommandFlags.DemandMaster); } - using (Track.StartInternalTrace("event-source.consumer.delay", t => t.Add("delay", releaseDelay))) + using (Track.StartInternalTrace("event-source.consumer.release.delay", + t => t.Add("delay", releaseDelay) + .Add("URI", uri))) { // let other potential consumer the chance of getting ownership await Task.Delay(releaseDelay, cancellationToken); @@ -800,7 +814,7 @@ await db.StreamClaimAsync(plan.FullUri(), catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { await _connFactory.CreateConsumerGroupIfNotExistsAsync( - key, + uri, plan.ConsumerGroup, logger, cancellationToken); } @@ -1110,7 +1124,8 @@ private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationTo var newDelay = cfg.CalcNextDelay(previousDelay, cfg); var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); newDelay = TimeSpan.FromMilliseconds(limitDelay); - using (Track.StartInternalTrace("event-source.consumer.delay.when-empty-queue", t => t.Add("delay", newDelay))) + using (Track.StartInternalTrace("event-source.consumer.delay.when-empty-queue", + t => t.Add("delay", newDelay))) { await Task.Delay(newDelay, cancellationToken); } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs index 5766891f..aad8c6ff 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs @@ -10,5 +10,4 @@ internal class Telemetry //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", // ""); - } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index a2f7a09c..1b39aa46 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.Metrics; +using System.Diagnostics; +using System.Diagnostics.Metrics; using Microsoft.Extensions.Logging; @@ -13,6 +14,7 @@ namespace EventSourcing.Backbone.Private /// public static class RedisCommonProviderExtensions { + private const int DELAY_ON_MISSING_KEY = 5; private const int MIN_DELAY = 2; private const int SPIN_LIMIT = 30; private const int MAX_DELAY = 3_000; @@ -28,29 +30,30 @@ public static class RedisCommonProviderExtensions /// Creates the consumer group if not exists asynchronous. ///
/// The connection factory. - /// The event source key. + /// The event source URI. /// The consumer group. /// The logger. /// The cancellation token. /// public static async Task CreateConsumerGroupIfNotExistsAsync( this IEventSourceRedisConnectionFactory connFactory, - string eventSourceKey, + string uri, string consumerGroup, ILogger logger, CancellationToken cancellationToken) { StreamGroupInfo[] groupsInfo = Array.Empty(); using var track = Track.StartInternalTrace("event-source.consumer.create-consumer-group", t => t.Add("group-name", consumerGroup)); - CreateConsumerGroupCounter.Add(1); - + CreateConsumerGroupCounter.WithTag("URI", uri).WithTag("consumer-group", consumerGroup).Add(1); int delay = MIN_DELAY; bool exists = false; int tryNumber = 0; + var retryCounter = CreateConsumerGroupRetryCounter.WithTag("URI", uri).WithTag("consumer-group", consumerGroup); + var missingCounter = KeyMissingCounter.WithTag("URI", uri).WithTag("consumer-group", consumerGroup); while (groupsInfo.Length == 0) { if (tryNumber != 0) - CreateConsumerGroupRetryCounter.Add(1); + retryCounter.Add(1); tryNumber++; IConnectionMultiplexer conn = await connFactory.GetAsync(cancellationToken); @@ -79,18 +82,11 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( #region Validation (if key exists) - if (!await db.KeyExistsAsync(eventSourceKey, + if (!await db.KeyExistsAsync(uri, flags: CommandFlags.DemandMaster)) { - KeyMissingCounter.Add(1); - //using (Track.StartInternalTrace("event-source.consumer.delay.key-not-exists", - // t => t.Add("delay", KEY_NOT_EXISTS_DELAY) - // .Add("try-number", tryNumber) - // .Add("group-name", consumerGroup))) ; - //{ - // // producer didn't produce anything yet - // await Task.Delay(KEY_NOT_EXISTS_DELAY); - //} + missingCounter.Add(1); + await Task.Delay(DELAY_ON_MISSING_KEY); if (tryNumber == 0 || tryNumber > SPIN_LIMIT) logger.LogDebug("Key not exists (yet): {info}", CurrentInfo()); continue; @@ -105,7 +101,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { using var lk = await _lock.AcquireAsync(cancellationToken); groupsInfo = await db.StreamGroupInfoAsync( - eventSourceKey, + uri, flags: CommandFlags.DemandMaster); exists = groupsInfo.Any(m => m.Name == consumerGroup); } @@ -114,7 +110,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( catch (RedisServerException ex) { - if (await db.KeyExistsAsync(eventSourceKey, + if (await db.KeyExistsAsync(uri, flags: CommandFlags.DemandMaster)) { logger.LogWarning(ex, "Create Consumer Group If Not Exists: failed. {info}", CurrentInfo()); @@ -149,7 +145,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( .Add("group-name", consumerGroup))) ; { using var lk = await _lock.AcquireAsync(cancellationToken); - if (await db.StreamCreateConsumerGroupAsync(eventSourceKey, + if (await db.StreamCreateConsumerGroupAsync(uri, consumerGroup, StreamPosition.Beginning, flags: CommandFlags.DemandMaster)) @@ -201,7 +197,7 @@ unexpected failure string CurrentInfo() => @$" Try number: {tryNumber} -Stream key: {eventSourceKey} +Stream key: {uri} Consumer Group: {consumerGroup} Is Connected: {db.Multiplexer.IsConnected} Configuration: {db.Multiplexer.Configuration} diff --git a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj index f891aa7b..2ff1b791 100644 --- a/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj +++ b/Channels/S3/EventSourcing.Backbone.Channels.S3StoreProvider.Common/EventSourcing.Backbone.Channels.S3StoreProvider.Common.csproj @@ -15,7 +15,7 @@ - + diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs index 74ffb95f..6400de76 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerBase.EventSourceSubscriber.cs @@ -189,7 +189,7 @@ private async ValueTask ConsumingAsync( if (hasProcessed) { if (behavior == AckBehavior.OnSucceed) - await ack.AckAsync(); + await ack.AckAsync(AckBehavior.OnSucceed); } //else //{ @@ -230,7 +230,7 @@ private async ValueTask ConsumingAsync( { if (Plan.Options.AckBehavior == AckBehavior.OnFinally && partialBehavior != PartialConsumerBehavior.Sequential) { - await ack.AckAsync(); + await ack.AckAsync(Plan.Options.AckBehavior); } #region Validation Max Messages Limit diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs index 184c08e1..afdd8109 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs @@ -1,4 +1,9 @@ -using Microsoft.Extensions.Logging; +using System; +using System.Diagnostics.Metrics; + +using Microsoft.Extensions.Logging; + +using static EventSourcing.Backbone.Telemetry; namespace EventSourcing.Backbone { @@ -10,27 +15,35 @@ namespace EventSourcing.Backbone public class AckOnce : IAck { private static readonly Func NON_FN = (_) => ValueTask.CompletedTask; + private readonly string _uri; private readonly Func _ackAsync; private readonly Func _cancelAsync; private readonly AckBehavior _behavior; private readonly ILogger _logger; private int _ackCount = 0; + private static readonly Counter AckCounter = Metrics.CreateCounter("event-source.consumer.event.ack", "count", + "Event's message handling acknowledge count"); + private static readonly Counter AbortCounter = Metrics.CreateCounter("event-source.consumer.event.abort", "count", + "Event's message handling aborted (cancel) count"); #region Ctor /// /// Initializes a new instance. /// + /// The URI. /// The ack. - /// The cancel. /// The behavior. /// The logger. + /// The cancel. public AckOnce( + string uri, Func ackAsync, AckBehavior behavior, ILogger logger, Func? cancelAsync = null) { + _uri = uri; _ackAsync = ackAsync; _cancelAsync = cancelAsync ?? NON_FN; _behavior = behavior; @@ -50,11 +63,12 @@ public AckOnce( public async ValueTask AckAsync(AckBehavior cause) { int count = Interlocked.Increment(ref _ackCount); + if (count != 1) + return; try { - if (count == 1) - await _ackAsync(cause); - + AckCounter.WithTag("URI", _uri).WithTag("cause", cause).Add(1); + await _ackAsync(cause); } catch (Exception ex) { @@ -78,11 +92,12 @@ public async ValueTask AckAsync(AckBehavior cause) public async ValueTask CancelAsync(AckBehavior cause) { int count = Interlocked.Increment(ref _ackCount); + if (count != 1) + return; try { - if (count == 1) - await _cancelAsync(cause); - + AbortCounter.WithTag("URI", _uri).WithTag("cause", cause).Add(1); + await _cancelAsync(cause); } catch (Exception ex) { diff --git a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs index 945fa6c2..266c14d7 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/ConsumerMetadata.cs @@ -94,7 +94,7 @@ public static implicit operator Metadata(ConsumerMetadata? instance) /// The cause of the cancellation. /// /// Must be execute from a consuming scope (i.e. method call invoked by the consumer's event processing). - public ValueTask CancelAsync(AckBehavior cause = AckBehavior.Manual) => Ack.Current.AckAsync(cause); + public ValueTask CancelAsync(AckBehavior cause = AckBehavior.Manual) => Ack.Current.CancelAsync(cause); #endregion // AckAsync diff --git a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs index 6308f36e..faccf71f 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs @@ -23,4 +23,7 @@ public static class EventSourceConstants JsonMemoryBytesConverterFactory.Default } }; + + public const string TELEMETRY_SOURCE = "event-source"; + } diff --git a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj index 8433008c..6b9f0fe3 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj +++ b/EventSourcing.Backbone.Abstractions/EventSourcing.Backbone.Abstractions.csproj @@ -19,7 +19,7 @@ - + diff --git a/EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs b/EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs index 9693e343..6cb6139d 100644 --- a/EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs +++ b/EventSourcing.Backbone.Abstractions/JsonDataSerializer.cs @@ -3,35 +3,34 @@ using static System.Text.Json.Extension.Constants; -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone; + +/// +/// Json serializer (this is the default serializer) +/// +/// +internal class JsonDataSerializer : IDataSerializer { - /// - /// Json serializer (this is the default serializer) - /// - /// - internal class JsonDataSerializer : IDataSerializer - { - private readonly JsonSerializerOptions _options; + private readonly JsonSerializerOptions _options; - public JsonDataSerializer(JsonSerializerOptions? options = null) - { - _options = options ?? SerializerOptions; - } + public JsonDataSerializer(JsonSerializerOptions? options = null) + { + _options = options ?? SerializerOptions; + } #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning disable CS8603 // Possible null reference return. - T IDataSerializer.Deserialize(ReadOnlyMemory serializedData) - { - T result = JsonSerializer.Deserialize(serializedData.Span, _options); - return result; - } + T IDataSerializer.Deserialize(ReadOnlyMemory serializedData) + { + T result = JsonSerializer.Deserialize(serializedData.Span, _options); + return result; + } #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. #pragma warning restore CS8603 // Possible null reference return. - ReadOnlyMemory IDataSerializer.Serialize(T item) - { - string result = JsonSerializer.Serialize(item, _options); - return Encoding.UTF8.GetBytes(result).AsMemory(); - } + ReadOnlyMemory IDataSerializer.Serialize(T item) + { + string result = JsonSerializer.Serialize(item, _options); + return Encoding.UTF8.GetBytes(result).AsMemory(); } } \ No newline at end of file diff --git a/EventSourcing.Backbone.Abstractions/Telemetry.cs b/EventSourcing.Backbone.Abstractions/Telemetry.cs new file mode 100644 index 00000000..72b514e9 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/Telemetry.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; +using System.Diagnostics.Metrics; + +namespace EventSourcing.Backbone; + +internal class Telemetry +{ + public readonly static Meter Metrics = new(EventSourceConstants.TELEMETRY_SOURCE); + //public static readonly ActivitySource Track = new ActivitySource(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); + + //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", + // ""); +} diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs b/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs index 11380b4d..f425606f 100644 --- a/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs +++ b/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs @@ -79,6 +79,7 @@ public static void InjectTelemetryTags(this Metadata meta, Activity? activity) // See: // * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#messaging-attributes + activity?.SetTag("event-source.env", meta.Environment); activity?.SetTag("event-source.uri", meta.Uri); activity?.SetTag("event-source.operation", meta.Operation); activity?.SetTag("event-source.message-id", meta.MessageId); diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index b6cf688b..73833baa 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -64,7 +64,8 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( var sources = new[] { (string)env, ProducerChannelConstants.REDIS_CHANNEL_SOURCE, ConsumerChannelConstants.REDIS_CHANNEL_SOURCE, - RedisChannelConstants.REDIS_CHANNEL_SOURCE }; + RedisChannelConstants.REDIS_CHANNEL_SOURCE, + EventSourceConstants.TELEMETRY_SOURCE }; tracerProviderBuilder .AddSource(sources) @@ -114,14 +115,15 @@ public static OpenTelemetryBuilder WithEventSourcingMetrics( { builder.WithMetrics(metricsProviderBuilder => { + injection?.Invoke(metricsProviderBuilder); metricsProviderBuilder - .ConfigureResource(resource => resource.AddService(env) - .AddService(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE)) + //.ConfigureResource(resource => resource.AddService(env) + // .AddService(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE)) .AddMeter(env, ProducerChannelConstants.REDIS_CHANNEL_SOURCE, ConsumerChannelConstants.REDIS_CHANNEL_SOURCE, - RedisChannelConstants.REDIS_CHANNEL_SOURCE); - injection?.Invoke(metricsProviderBuilder); + RedisChannelConstants.REDIS_CHANNEL_SOURCE, + EventSourceConstants.TELEMETRY_SOURCE); }); return builder; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 9aa4a187..5cd0a41a 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -104,6 +104,8 @@ app.UseSwaggerUI(); } +app.UseOpenTelemetryPrometheusScrapingEndpoint(); + app.UseHttpsRedirection(); app.MapControllers(); diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index b4d4169e..8dcbeb70 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -19,7 +19,7 @@ - + From bcd52aa84fc5075ba9fad9362bbd93d9d36a1c8e Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:54:41 +0300 Subject: [PATCH 173/178] feat: telemetry --- .../RedisConsumerChannel.cs | 126 +++++++++--------- .../Telemetry.cs | 13 -- .../RedisProducerChannel.cs | 8 +- .../Telemetry.cs | 10 -- .../EventSourceRedisConnectionFactory.cs | 4 +- .../RedisCommonProviderExtensions.cs | 59 ++++---- .../Telemetry.cs | 11 -- .../Consumer/Ack/AckOnce.cs | 6 +- .../EventSourceTelemetry.cs | 13 ++ .../Telemetry.cs | 13 -- EventSourcing.Backbone.sln | 1 + ...g.Backbone.OpenTelemetry.Extensions.csproj | 10 -- .../EventSourcingOtel.cs | 8 -- 13 files changed, 121 insertions(+), 161 deletions(-) delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs create mode 100644 EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs delete mode 100644 EventSourcing.Backbone.Abstractions/Telemetry.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index c8611d7b..e9a8c2c8 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -15,7 +15,7 @@ using static System.Math; using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static EventSourcing.Backbone.Channels.RedisProvider.Telemetry; +using static EventSourcing.Backbone.Private.EventSourceTelemetry; // TODO: [bnaya 2021-07] MOVE TELEMETRY TO THE BASE CLASSES OF PRODUCER / CONSUME @@ -26,15 +26,15 @@ namespace EventSourcing.Backbone.Channels.RedisProvider; ///
internal class RedisConsumerChannel : IConsumerChannelProvider { - private static readonly Counter StealCountCounter = Metrics.CreateCounter("event-source.consumer.events-stealing", "count", + private static readonly Counter StealCountCounter = EMeter.CreateCounter("event-source.consumer.events-stealing", "count", "Attempt to get stale events (messages) from other consumer (which assumed malfunction)"); - private static readonly Counter StealAmountCounter = Metrics.CreateCounter("event-source.consumer.events-stealing.messages", "count", + private static readonly Counter StealAmountCounter = EMeter.CreateCounter("event-source.consumer.events-stealing.messages", "count", "Attempt to get stale events (messages) from other consumer (which assumed malfunction)"); - private static readonly Counter ConcumeBatchCountCounter = Metrics.CreateCounter("event-source.consumer.batch", "count", + private static readonly Counter ConcumeBatchCountCounter = EMeter.CreateCounter("event-source.consumer.batch", "count", "count of the number of non empty consuming batches form the stream provider"); - private static readonly Counter ConcumeBatchSumCounter = Metrics.CreateCounter("event-source.consumer.batch.events", "count", + private static readonly Counter ConcumeBatchSumCounter = EMeter.CreateCounter("event-source.consumer.batch.events", "count", "Sum of total consuming events (messages) before process"); - private static readonly Counter ConcumeBatchFailureCounter = Metrics.CreateCounter("event-source.consumer.batch.failure", "count", + private static readonly Counter ConcumeBatchFailureCounter = EMeter.CreateCounter("event-source.consumer.batch.failure", "count", "batch reading failure"); private const string BEGIN_OF_STREAM = "0000000000000"; @@ -219,7 +219,10 @@ private async Task SubsribeToSingleAsync( var claimingTrigger = options.ClaimingTrigger; var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - string uri = plan.FullUri(); + Env env = plan.Environment; + string uri = plan.Uri; + string fullUri = plan.FullUri(); + bool isFirstBatchOrFailure = true; CommandFlags flags = CommandFlags.None; @@ -230,13 +233,13 @@ private async Task SubsribeToSingleAsync( #region await db.CreateConsumerGroupIfNotExistsAsync(...) await _connFactory.CreateConsumerGroupIfNotExistsAsync( - uri, + plan, RedisChannelConstants.NONE_CONSUMER, logger, cancellationToken); await _connFactory.CreateConsumerGroupIfNotExistsAsync( - uri, + plan, plan.ConsumerGroup, logger, cancellationToken); @@ -248,7 +251,7 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( TimeSpan delay = TimeSpan.Zero; int emptyBatchCount = 0; - using (Track.StartActivity("event-source.consumer.loop", ActivityKind.Server)) + using (ETracer.StartActivity("event-source.consumer.loop", ActivityKind.Server)) { while (!cancellationToken.IsCancellationRequested) { @@ -349,7 +352,7 @@ async Task HandleBatchBreakerAsync(CancellationToken ct) #endregion // Metadata meta = ... ActivityContext parentContext = EventSourceTelemetryExtensions.ExtractSpan(channelMeta, ExtractTraceContext); - using var activity = Track.StartConsumerTrace(meta, parentContext); + using var activity = ETracer.StartConsumerTrace(meta, parentContext); #region IEnumerable ExtractTraceContext(Dictionary entries, string key) @@ -382,16 +385,18 @@ IEnumerable ExtractTraceContext(Dictionary entri int local = i; var cancellableIds = results[local..].Select(m => m.Id); var ack = new AckOnce( - uri, + fullUri, async (cause) => { - Activity.Current?.AddEvent("event-source.consumer.event.ack", t => t.Add("cause", cause).Add("URI", uri)); + Activity.Current?.AddEvent("event-source.consumer.event.ack", + t => PrepareTrace(t).Add("cause", cause)); await AckAsync(result.Id); }, plan.Options.AckBehavior, logger, async (cause) => { - Activity.Current?.AddEvent("event-source.consumer.event.cancel", t => t.Add("cause", cause).Add("URI", uri)); + Activity.Current?.AddEvent("event-source.consumer.event.cancel", + t => PrepareTrace(t).Add("cause", cause)); batchCancellation.CancelSafe(); // cancel forward await CancelAsync(cancellableIds); }); @@ -427,7 +432,7 @@ IEnumerable ExtractTraceContext(Dictionary entri #endregion // var announcement = new Announcement(...) bool succeed; - using (var execActivity = Track.StartInternalTrace("event-source.consumer.execute-event")) + using (var execActivity = ETracer.StartInternalTrace("event-source.consumer.execute-event")) { succeed = await func(announcement, ack); execActivity?.SetTag("succeed", succeed); @@ -442,11 +447,11 @@ IEnumerable ExtractTraceContext(Dictionary entri // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) { - using (Track.StartInternalTrace("event-source.consumer.release-events-on-failure")) + using (ETracer.StartInternalTrace("event-source.consumer.release-events-on-failure")) { RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet - //using (Track.StartInternalTrace("event-source.consumer.delay", t => t.Add("duration", releaseDelay))) + //using (ETracer.StartInternalTrace("event-source.consumer.delay", t => t.Add("duration", releaseDelay))) //{ // // let other potential consumer the chance of getting ownership // await Task.Delay(releaseDelay, ct); @@ -483,18 +488,16 @@ async Task ReadBatchAsync() { isFirstBatchOrFailure = false; string group = plan.ConsumerGroup; - using var activity = Track.StartInternalTrace("event-source.consumer.read-batch", - t => t - .Add("consumer-group", group) - .Add("URI", uri) - ); + using var activity = ETracer.StartInternalTrace("event-source.consumer.read-batch", + t => PrepareTrace(t) + .Add("consumer-group", group)); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { values = await db.StreamReadGroupAsync( - uri, + fullUri, group, plan.ConsumerName, position: StreamPosition.NewMessages, @@ -502,27 +505,27 @@ async Task ReadBatchAsync() flags: flags) .WithCancellation(ct, () => Array.Empty()) .WithCancellation(cancellationToken, () => Array.Empty()); - ConcumeBatchCountCounter.WithTag("URI", uri).Add(1); + PrepareMeter(ConcumeBatchCountCounter).Add(1); activity?.SetTag("count", values.Length); - ConcumeBatchSumCounter.WithTag("URI", uri).Add(values.Length); + PrepareMeter(ConcumeBatchSumCounter).Add(values.Length); } #region Exception Handling catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { - ConcumeBatchFailureCounter.WithTag("URI", uri).Add(1); + PrepareMeter(ConcumeBatchFailureCounter).Add(1); logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( - uri, + plan, plan.ConsumerGroup, logger, cancellationToken); } catch (RedisServerException ex) { - ConcumeBatchFailureCounter.WithTag("URI", uri).Add(1); + PrepareMeter(ConcumeBatchFailureCounter).Add(1); logger.LogWarning(ex, ex.Message); await _connFactory.CreateConsumerGroupIfNotExistsAsync( - uri, + plan, plan.ConsumerGroup, logger, cancellationToken); } @@ -538,12 +541,12 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( catch (RedisTimeoutException ex) { - logger.LogWarning(ex, "Event source [{source}] by [{consumer}]: Timeout", uri, plan.ConsumerName); + logger.LogWarning(ex, "Event source [{source}] by [{consumer}]: Timeout", fullUri, plan.ConsumerName); return Array.Empty(); } catch (Exception ex) { - logger.LogError(ex, "Fail to read from event source [{source}] by [{consumer}]", uri, plan.ConsumerName); + logger.LogError(ex, "Fail to read from event source [{source}] by [{consumer}]", fullUri, plan.ConsumerName); return Array.Empty(); } @@ -562,14 +565,14 @@ async Task ReadSelfPending() if (!isFirstBatchOrFailure) return values; - using var _ = Track.StartInternalTrace("event-source.consumer.self-pending"); + using var _ = ETracer.StartInternalTrace("event-source.consumer.self-pending"); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); try { StreamPendingMessageInfo[] pendMsgInfo = await db.StreamPendingMessagesAsync( - uri, + fullUri, plan.ConsumerGroup, options.BatchSize, plan.ConsumerName, @@ -580,7 +583,7 @@ async Task ReadSelfPending() .Select(m => m.MessageId).ToArray(); if (ids.Length != 0) { - values = await db.StreamClaimAsync(uri, + values = await db.StreamClaimAsync(fullUri, plan.ConsumerGroup, plan.ConsumerName, 0, @@ -598,7 +601,7 @@ async Task ReadSelfPending() catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { await _connFactory.CreateConsumerGroupIfNotExistsAsync( - uri, + plan, plan.ConsumerGroup, logger, cancellationToken); return Array.Empty(); @@ -622,17 +625,16 @@ async Task ClaimStaleMessages( if (values.Length != 0) return values; if (emptyBatchCount < claimingTrigger.EmptyBatchCount) return values; - string uri = plan.FullUri(); - using var _ = Track.StartInternalTrace("event-source.consumer.stale-events"); + using var _ = ETracer.StartInternalTrace("event-source.consumer.stale-events"); try { IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); StreamPendingInfo pendingInfo; - using (Track.StartInternalTrace("event-source.consumer.events-stealing.pending")) + using (ETracer.StartInternalTrace("event-source.consumer.events-stealing.pending")) { - pendingInfo = await db.StreamPendingAsync(uri, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); + pendingInfo = await db.StreamPendingAsync(fullUri, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); } - StealCountCounter.WithTag("URI", uri).Add(1); + PrepareMeter(StealCountCounter).Add(1); foreach (var c in pendingInfo.Consumers) { var self = c.Name == plan.ConsumerName; @@ -640,12 +642,11 @@ async Task ClaimStaleMessages( try { StreamPendingMessageInfo[] pendMsgInfo; - using (Track.StartInternalTrace("event-source.consumer.events-stealing.pending-events", - t => t.Add("from-consumer", c.Name) - .Add("URI", uri))) + using (ETracer.StartInternalTrace("event-source.consumer.events-stealing.pending-events", + t => PrepareTrace(t).Add("from-consumer", c.Name))) { pendMsgInfo = await db.StreamPendingMessagesAsync( - uri, + fullUri, plan.ConsumerGroup, 10, c.Name, @@ -666,14 +667,15 @@ async Task ClaimStaleMessages( #endregion // Log int count = ids.Length; - StealAmountCounter.WithTag("from-consumer", c.Name).WithTag("URI", uri).Add(count); + PrepareMeter(StealAmountCounter).WithTag("from-consumer", c.Name) + .Add(count); // will claim events only if older than _setting.ClaimingTrigger.MinIdleTime - using (Track.StartInternalTrace("event-source.consumer.events-stealing.claim", - t => t.Add("from-consumer", c.Name) - .Add("message-count", count) - .Add("URI", uri))) + using (ETracer.StartInternalTrace("event-source.consumer.events-stealing.claim", + t => PrepareTrace(t) + .Add("from-consumer", c.Name) + .Add("message-count", count))) { - values = await db.StreamClaimAsync(uri, + values = await db.StreamClaimAsync(fullUri, plan.ConsumerGroup, c.Name, minIdleTime, @@ -731,7 +733,7 @@ async ValueTask AckAsync(RedisValue messageId) IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); // release the event (won't handle again in the future) - await db.StreamAcknowledgeAsync(uri, + await db.StreamAcknowledgeAsync(fullUri, plan.ConsumerGroup, messageId, flags: CommandFlags.DemandMaster); @@ -782,10 +784,8 @@ async Task ReleaseAsync(RedisValue[] freeTargets) IDatabaseAsync db = conn.GetDatabase(); try { - using (Track.StartInternalTrace("event-source.consumer.release-ownership", - t => t - .Add("consumer-group", plan.ConsumerGroup) - .Add("URI", uri))) + using (ETracer.StartInternalTrace("event-source.consumer.release-ownership", + t => PrepareTrace(t).Add("consumer-group", plan.ConsumerGroup))) { await db.StreamClaimAsync(plan.FullUri(), plan.ConsumerGroup, @@ -794,9 +794,8 @@ await db.StreamClaimAsync(plan.FullUri(), freeTargets, flags: CommandFlags.DemandMaster); } - using (Track.StartInternalTrace("event-source.consumer.release.delay", - t => t.Add("delay", releaseDelay) - .Add("URI", uri))) + using (ETracer.StartInternalTrace("event-source.consumer.release.delay", + t => PrepareTrace(t).Add("delay", releaseDelay))) { // let other potential consumer the chance of getting ownership await Task.Delay(releaseDelay, cancellationToken); @@ -814,7 +813,7 @@ await db.StreamClaimAsync(plan.FullUri(), catch (RedisServerException ex) when (ex.Message.StartsWith("NOGROUP")) { await _connFactory.CreateConsumerGroupIfNotExistsAsync( - uri, + plan, plan.ConsumerGroup, logger, cancellationToken); } @@ -823,6 +822,9 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( } #endregion // ReleaseAsync + + ITagAddition PrepareTrace(ITagAddition t) => t.Add("uri", uri).Add("env", env); + ICounterBuilder PrepareMeter(Counter t) => t.WithTag("uri", uri).WithTag("env", env); } #endregion // SubsribeToSingleAsync @@ -1090,7 +1092,7 @@ private async ValueTask GetBucketAsync( { foreach (var strategy in strategies) { - using (Track.StartInternalTrace($"event-source.consumer.{strategy.Name}-storage.{storageType}.get")) + using (ETracer.StartInternalTrace($"event-source.consumer.{strategy.Name}-storage.{storageType}.get")) { bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } @@ -1098,7 +1100,7 @@ private async ValueTask GetBucketAsync( } else { - using (Track.StartInternalTrace($"event-source.consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) + using (ETracer.StartInternalTrace($"event-source.consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) { bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } @@ -1124,7 +1126,7 @@ private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationTo var newDelay = cfg.CalcNextDelay(previousDelay, cfg); var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); newDelay = TimeSpan.FromMilliseconds(limitDelay); - using (Track.StartInternalTrace("event-source.consumer.delay.when-empty-queue", + using (ETracer.StartInternalTrace("event-source.consumer.delay.when-empty-queue", t => t.Add("delay", newDelay))) { await Task.Delay(newDelay, cancellationToken); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs deleted file mode 100644 index aad8c6ff..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/Telemetry.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.Metrics; - -namespace EventSourcing.Backbone.Channels.RedisProvider; - -internal class Telemetry -{ - public readonly static Meter Metrics = new(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); - public static readonly ActivitySource Track = new ActivitySource(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); - - //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", - // ""); -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index d42954f7..e3884c0e 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -14,7 +14,7 @@ using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static EventSourcing.Backbone.Channels.RedisProvider.Telemetry; +using static EventSourcing.Backbone.Private.EventSourceTelemetry; namespace EventSourcing.Backbone.Channels.RedisProvider; @@ -66,7 +66,7 @@ public async ValueTask SendAsync( { Metadata meta = payload.Metadata; string id = meta.MessageId; - using var activity = Track.StartInternalTrace($"event-source.producer.{meta.Operation}.process", + using var activity = ETracer.StartInternalTrace($"event-source.producer.{meta.Operation}.process", t => t.Add("env", meta.Environment) .Add("message-id", id)); @@ -99,7 +99,7 @@ async Task LocalStreamAddAsync() await LocalStoreBucketAsync(EventBucketCategories.Interceptions); var telemetryBuilder = commonEntries.ToBuilder(); - using Activity? activity = Track.StartProducerTrace(meta); + using Activity? activity = ETracer.StartProducerTrace(meta); activity.InjectSpan(telemetryBuilder, LocalInjectTelemetry); var entries = telemetryBuilder.ToArray(); @@ -150,7 +150,7 @@ async ValueTask LocalStoreBucketAsync(EventBucketCategories storageType) async ValueTask SaveBucketAsync(IProducerStorageStrategy strategy) { - using (Track.StartInternalTrace($"event-source.producer.{strategy.Name}-storage.{storageType}.set")) + using (ETracer.StartInternalTrace($"event-source.producer.{strategy.Name}-storage.{storageType}.set")) { IImmutableDictionary metaItems = await strategy.SaveBucketAsync(id, bucket, storageType, meta); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs deleted file mode 100644 index d71e8fc9..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/Telemetry.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.Metrics; - -namespace EventSourcing.Backbone.Channels.RedisProvider; - -internal class Telemetry -{ - public readonly static Meter Metrics = new(ProducerChannelConstants.REDIS_CHANNEL_SOURCE); - public static readonly ActivitySource Track = new ActivitySource(ProducerChannelConstants.REDIS_CHANNEL_SOURCE); -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs index 327150ce..32e267f2 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs @@ -6,7 +6,7 @@ using StackExchange.Redis; using static EventSourcing.Backbone.Channels.RedisProvider.Common.RedisChannelConstants; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.Telemetry; +using static EventSourcing.Backbone.Private.EventSourceTelemetry; #pragma warning disable S3881 // "IDisposable" should be implemented correctly #pragma warning disable S2953 // Methods named "Dispose" should implement "IDisposable.Dispose" @@ -31,7 +31,7 @@ public class EventSourceRedisConnectionFactory : IEventSourceRedisConnectionFact private DateTime _lastResetConnection = DateTime.Now; private int _reconnectTry = 0; private const string CHANGE_CONN = "redis-change-connection"; - private static readonly Counter ReConnectCounter = Metics.CreateCounter(CHANGE_CONN, "count", + private static readonly Counter ReConnectCounter = EMeter.CreateCounter(CHANGE_CONN, "count", "count how many time the connection was re-create"); #region Ctor diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 1b39aa46..0d424e49 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -5,7 +5,7 @@ using StackExchange.Redis; -using static EventSourcing.Backbone.Channels.RedisProvider.Common.Telemetry; +using static EventSourcing.Backbone.Private.EventSourceTelemetry; namespace EventSourcing.Backbone.Private { @@ -18,9 +18,9 @@ public static class RedisCommonProviderExtensions private const int MIN_DELAY = 2; private const int SPIN_LIMIT = 30; private const int MAX_DELAY = 3_000; - private static Counter KeyMissingCounter = Metics.CreateCounter("event-source.key-missing", "count", "count missing key events"); - private static Counter CreateConsumerGroupCounter = Metics.CreateCounter("event-source.create-consumer-group", "count", "creating a consumer group"); - private static Counter CreateConsumerGroupRetryCounter = Metics.CreateCounter("event-source.create-consumer-group-retry", "count", "retries of creating a consumer group"); + private static Counter KeyMissingCounter = EMeter.CreateCounter("event-source.key-missing", "count", "count missing key events"); + private static Counter CreateConsumerGroupCounter = EMeter.CreateCounter("event-source.create-consumer-group", "count", "creating a consumer group"); + private static Counter CreateConsumerGroupRetryCounter = EMeter.CreateCounter("event-source.create-consumer-group-retry", "count", "retries of creating a consumer group"); private static readonly AsyncLock _lock = new AsyncLock(TimeSpan.FromSeconds(20)); @@ -30,6 +30,7 @@ public static class RedisCommonProviderExtensions /// Creates the consumer group if not exists asynchronous. ///
/// The connection factory. + /// The environment. /// The event source URI. /// The consumer group. /// The logger. @@ -37,19 +38,25 @@ public static class RedisCommonProviderExtensions /// public static async Task CreateConsumerGroupIfNotExistsAsync( this IEventSourceRedisConnectionFactory connFactory, - string uri, + IConsumerPlan plan, string consumerGroup, ILogger logger, CancellationToken cancellationToken) { + Env env = plan.Environment; + string uri = plan.Uri; + string fullUri = plan.FullUri(); + StreamGroupInfo[] groupsInfo = Array.Empty(); - using var track = Track.StartInternalTrace("event-source.consumer.create-consumer-group", t => t.Add("group-name", consumerGroup)); - CreateConsumerGroupCounter.WithTag("URI", uri).WithTag("consumer-group", consumerGroup).Add(1); + using var track = ETracer.StartInternalTrace("event-source.consumer.create-consumer-group", + t => PrepareTrace(t)); + + PrepareMeter(CreateConsumerGroupCounter).Add(1); int delay = MIN_DELAY; bool exists = false; int tryNumber = 0; - var retryCounter = CreateConsumerGroupRetryCounter.WithTag("URI", uri).WithTag("consumer-group", consumerGroup); - var missingCounter = KeyMissingCounter.WithTag("URI", uri).WithTag("consumer-group", consumerGroup); + var retryCounter = PrepareMeter(CreateConsumerGroupRetryCounter); + var missingCounter = PrepareMeter(KeyMissingCounter); while (groupsInfo.Length == 0) { if (tryNumber != 0) @@ -65,10 +72,10 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( if (tryNumber > SPIN_LIMIT) { delay = Math.Min(delay * 2, MAX_DELAY); - using (Track.StartInternalTrace("event-source.consumer.delay.key-not-exists", - t => t.Add("delay", delay) - .Add("try-number", tryNumber) - .Add("group-name", consumerGroup))) ; + using (ETracer.StartInternalTrace("event-source.consumer.delay.key-not-exists", + t => PrepareTrace(t) + .Add("delay", delay) + .Add("try-number", tryNumber))) { await Task.Delay(delay); } @@ -82,7 +89,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( #region Validation (if key exists) - if (!await db.KeyExistsAsync(uri, + if (!await db.KeyExistsAsync(fullUri, flags: CommandFlags.DemandMaster)) { missingCounter.Add(1); @@ -94,14 +101,13 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( #endregion // Validation (if key exists) - using (Track.StartInternalTrace("event-source.consumer.get-consumer-group-info", - t => t - .Add("try-number", tryNumber) - .Add("group-name", consumerGroup))) ; + using (ETracer.StartInternalTrace("event-source.consumer.get-consumer-group-info", + t => PrepareTrace(t) + .Add("try-number", tryNumber))) { using var lk = await _lock.AcquireAsync(cancellationToken); groupsInfo = await db.StreamGroupInfoAsync( - uri, + fullUri, flags: CommandFlags.DemandMaster); exists = groupsInfo.Any(m => m.Name == consumerGroup); } @@ -110,7 +116,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( catch (RedisServerException ex) { - if (await db.KeyExistsAsync(uri, + if (await db.KeyExistsAsync(fullUri, flags: CommandFlags.DemandMaster)) { logger.LogWarning(ex, "Create Consumer Group If Not Exists: failed. {info}", CurrentInfo()); @@ -139,13 +145,12 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { try { - using (Track.StartInternalTrace("event-source.consumer.create-consumer-group", - t => t - .Add("try-number", tryNumber) - .Add("group-name", consumerGroup))) ; + using (ETracer.StartInternalTrace("event-source.consumer.create-consumer-group", + t => PrepareTrace(t) + .Add("try-number", tryNumber))) { using var lk = await _lock.AcquireAsync(cancellationToken); - if (await db.StreamCreateConsumerGroupAsync(uri, + if (await db.StreamCreateConsumerGroupAsync(fullUri, consumerGroup, StreamPosition.Beginning, flags: CommandFlags.DemandMaster)) @@ -205,6 +210,10 @@ string CurrentInfo() => @$" #endregion // string CurrentInfo() } + + + ITagAddition PrepareTrace(ITagAddition t) => t.Add("uri", uri).Add("env", env).Add("group-name", consumerGroup); + ICounterBuilder PrepareMeter(Counter t) => t.WithTag("uri", uri).WithTag("env", env); } #endregion // CreateConsumerGroupIfNotExistsAsync diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs deleted file mode 100644 index 8cf8cd6b..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Telemetry.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.Metrics; - -namespace EventSourcing.Backbone.Channels.RedisProvider.Common; - - -internal class Telemetry -{ - public readonly static Meter Metics = new(RedisChannelConstants.REDIS_CHANNEL_SOURCE); - public static readonly ActivitySource Track = new ActivitySource(RedisChannelConstants.REDIS_CHANNEL_SOURCE); -} diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs index afdd8109..4094f1ea 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging; -using static EventSourcing.Backbone.Telemetry; +using static EventSourcing.Backbone.Private.EventSourceTelemetry; namespace EventSourcing.Backbone { @@ -21,9 +21,9 @@ public class AckOnce : IAck private readonly AckBehavior _behavior; private readonly ILogger _logger; private int _ackCount = 0; - private static readonly Counter AckCounter = Metrics.CreateCounter("event-source.consumer.event.ack", "count", + private static readonly Counter AckCounter = EMeter.CreateCounter("event-source.consumer.event.ack", "count", "Event's message handling acknowledge count"); - private static readonly Counter AbortCounter = Metrics.CreateCounter("event-source.consumer.event.abort", "count", + private static readonly Counter AbortCounter = EMeter.CreateCounter("event-source.consumer.event.abort", "count", "Event's message handling aborted (cancel) count"); #region Ctor diff --git a/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs b/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs new file mode 100644 index 00000000..eb3d16f5 --- /dev/null +++ b/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; +using System.Diagnostics.Metrics; + +namespace EventSourcing.Backbone.Private; + +public class EventSourceTelemetry +{ + public readonly static Meter EMeter = new(EventSourceConstants.TELEMETRY_SOURCE); + public static readonly ActivitySource ETracer = new ActivitySource(EventSourceConstants.TELEMETRY_SOURCE); + + //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", + // ""); +} diff --git a/EventSourcing.Backbone.Abstractions/Telemetry.cs b/EventSourcing.Backbone.Abstractions/Telemetry.cs deleted file mode 100644 index 72b514e9..00000000 --- a/EventSourcing.Backbone.Abstractions/Telemetry.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.Metrics; - -namespace EventSourcing.Backbone; - -internal class Telemetry -{ - public readonly static Meter Metrics = new(EventSourceConstants.TELEMETRY_SOURCE); - //public static readonly ActivitySource Track = new ActivitySource(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE); - - //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", - // ""); -} diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index f913f4da..20396ca7 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -222,6 +222,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {C1618305-4F0B-4AAA-8386-CA31DCBC5F59} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} + {EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2} = {2C4E653D-8B7A-48BA-A9DB-F9707BC04380} {6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106} = {EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2} {90B4EBE9-7CDB-42E0-B5E2-9FCAC4AD52CA} = {3FBE7FA1-700D-4B58-8569-15F533F761D1} {64FC6860-BD4C-4C11-BF6E-E99BB4D91723} = {75CE8A6F-D63B-491D-8834-8D3BA2127208} diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj index 79b634da..8ac3d16f 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions.csproj @@ -9,12 +9,6 @@ README.md - - - - - - \ @@ -40,9 +34,5 @@ - - - - diff --git a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs index 73833baa..6f7c3667 100644 --- a/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs +++ b/Extensions/EventSourcing.Backbone.OpenTelemetry.Extensions/EventSourcingOtel.cs @@ -1,6 +1,4 @@ using EventSourcing.Backbone; -using EventSourcing.Backbone.Channels; -using EventSourcing.Backbone.Channels.RedisProvider.Common; using Microsoft.Extensions.Hosting; @@ -62,9 +60,6 @@ public static OpenTelemetryBuilder WithEventSourcingTracing( .WithTracing(tracerProviderBuilder => { var sources = new[] { (string)env, - ProducerChannelConstants.REDIS_CHANNEL_SOURCE, - ConsumerChannelConstants.REDIS_CHANNEL_SOURCE, - RedisChannelConstants.REDIS_CHANNEL_SOURCE, EventSourceConstants.TELEMETRY_SOURCE }; tracerProviderBuilder @@ -120,9 +115,6 @@ public static OpenTelemetryBuilder WithEventSourcingMetrics( //.ConfigureResource(resource => resource.AddService(env) // .AddService(ConsumerChannelConstants.REDIS_CHANNEL_SOURCE)) .AddMeter(env, - ProducerChannelConstants.REDIS_CHANNEL_SOURCE, - ConsumerChannelConstants.REDIS_CHANNEL_SOURCE, - RedisChannelConstants.REDIS_CHANNEL_SOURCE, EventSourceConstants.TELEMETRY_SOURCE); }); From 0ee6cfbc9560d12fbc076e9ded9bc94294a4ddc2 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 25 Jun 2023 09:36:21 +0300 Subject: [PATCH 174/178] rfc: telemetry --- .editorconfig | 12 +++ .../ConsumerChannelConstants.cs | 2 +- .../RedisConsumerChannel.cs | 91 ++++++++++--------- .../ProducerChannelConstants.cs | 12 --- .../RedisProducerChannel.cs | 27 ++++-- .../RedisChannelConstants.cs | 1 - .../RedisCommonProviderExtensions.cs | 28 +++--- .../Builder/ConsumerPlan.cs | 24 ++++- .../ConsumerTelemetryExtensions.cs | 58 +++++++++++- Directory.Build.props | 2 +- .../Consumer/Ack/AckOnce.cs | 4 +- .../Entities/Announcement/Metadata.cs | 20 +++- EventSourcing.Backbone.Abstractions/Env.cs | 39 +++++++- .../EventSourceConstants.cs | 2 +- .../EventSourceTelemetry.cs | 3 - .../Interfaces/IPlanRoute.cs | 29 +++--- .../Producer/Plan/ProducerPlan.cs | 26 +++++- .../EventSourceTelemetryExtensions.cs | 10 +- EventSourcing.Backbone.sln | 1 - .../ProducerTelemetryExtensions.cs | 6 +- Tests/ConsoleTest/Constants.cs | 2 +- Tests/ConsoleTest/Program.cs | 4 +- .../AckPatternsTests.cs | 11 +-- .../EndToEndExplicitTests.cs | 2 +- .../EndToEndStressTests.cs | 2 +- .../EndToEndTests.cs | 11 ++- .../HelloWorld/HelloWorldTests.cs | 2 +- .../InheritanceTests.cs | 2 +- .../MigrationReceiverTest.cs | 4 +- .../MigrationTest.cs | 2 +- .../TestsBase.cs | 2 +- .../AspCoreExtensions.cs | 58 ++---------- .../Jobs/MigrationJob.cs | 6 +- .../OpenTelemetryExtensions.cs | 8 +- .../Program.cs | 2 +- 35 files changed, 324 insertions(+), 191 deletions(-) delete mode 100644 Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs diff --git a/.editorconfig b/.editorconfig index 0e6434b4..5039a0f7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,3 +5,15 @@ dotnet_diagnostic.HAA0101.severity = none # HAA0601: Value type to reference type conversion causing boxing allocation dotnet_diagnostic.HAA0601.severity = none + +# HAA0603: Delegate allocation from a method group +dotnet_diagnostic.HAA0603.severity = none + +# HAA0301: Closure Allocation Source +dotnet_diagnostic.HAA0301.severity = none + +# HAA0302: Display class allocation to capture closure +dotnet_diagnostic.HAA0302.severity = none + +# HAA0401: Possible allocation of reference type enumerator +dotnet_diagnostic.HAA0401.severity = none diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs index 5d0470a7..3f1b0a7d 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/ConsumerChannelConstants.cs @@ -8,5 +8,5 @@ internal static class ConsumerChannelConstants /// /// The name of redis consumer channel source /// - public const string REDIS_CHANNEL_SOURCE = "event-source-redis-consumer-channel"; + public const string REDIS_CHANNEL_SOURCE = "evt-src-redis-consumer-channel"; } diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index e9a8c2c8..85f5540a 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -4,6 +4,8 @@ using System.Runtime.CompilerServices; using System.Text.Json; +using Bnaya.Extensions.Common.Disposables; + using EventSourcing.Backbone.Building; using EventSourcing.Backbone.Channels.RedisProvider.Common; using EventSourcing.Backbone.Consumers; @@ -26,15 +28,17 @@ namespace EventSourcing.Backbone.Channels.RedisProvider; ///
internal class RedisConsumerChannel : IConsumerChannelProvider { - private static readonly Counter StealCountCounter = EMeter.CreateCounter("event-source.consumer.events-stealing", "count", + private static readonly Counter StealCountCounter = EMeter.CreateCounter("evt-src.sys.consumer.events-stealing", "count", "Attempt to get stale events (messages) from other consumer (which assumed malfunction)"); - private static readonly Counter StealAmountCounter = EMeter.CreateCounter("event-source.consumer.events-stealing.messages", "count", + private static readonly Counter StealAmountCounter = EMeter.CreateCounter("evt-src.sys.consumer.events-stealing.messages", "count", "Attempt to get stale events (messages) from other consumer (which assumed malfunction)"); - private static readonly Counter ConcumeBatchCountCounter = EMeter.CreateCounter("event-source.consumer.batch", "count", + private static readonly Counter ConcumeBatchCountCounter = EMeter.CreateCounter("evt-src.sys.consumer.batch", "count", "count of the number of non empty consuming batches form the stream provider"); - private static readonly Counter ConcumeBatchSumCounter = EMeter.CreateCounter("event-source.consumer.batch.events", "count", + private static readonly Counter ConcumeEventsCounter = EMeter.CreateCounter("evt-src.sys.consumer.events", "count", + "Sum of total consuming events (messages) before process"); + private static readonly Counter ConcumeEventsOperationCounter = EMeter.CreateCounter("evt-src.sys.consumer.events.operation", "count", "Sum of total consuming events (messages) before process"); - private static readonly Counter ConcumeBatchFailureCounter = EMeter.CreateCounter("event-source.consumer.batch.failure", "count", + private static readonly Counter ConcumeBatchFailureCounter = EMeter.CreateCounter("evt-src.sys.consumer.batch.failure", "count", "batch reading failure"); private const string BEGIN_OF_STREAM = "0000000000000"; @@ -219,9 +223,10 @@ private async Task SubsribeToSingleAsync( var claimingTrigger = options.ClaimingTrigger; var minIdleTime = (int)options.ClaimingTrigger.MinIdleTime.TotalMilliseconds; - Env env = plan.Environment; - string uri = plan.Uri; + var env = plan.Environment.ToDash(); + string uri = plan.Uri.ToDash(); string fullUri = plan.FullUri(); + string consumerGroup = plan.ConsumerGroup.ToDash(); bool isFirstBatchOrFailure = true; @@ -230,7 +235,7 @@ private async Task SubsribeToSingleAsync( ILogger logger = plan.Logger ?? _logger; - #region await db.CreateConsumerGroupIfNotExistsAsync(...) + #region await CreateConsumerGroupIfNotExistsAsync(...) await _connFactory.CreateConsumerGroupIfNotExistsAsync( plan, @@ -244,14 +249,14 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( logger, cancellationToken); - #endregion // await db.CreateConsumerGroupIfNotExistsAsync(...) + #endregion // await CreateConsumerGroupIfNotExistsAsync(...) int releaseDelay = INIT_RELEASE_DELAY; int bachSize = options.BatchSize; TimeSpan delay = TimeSpan.Zero; int emptyBatchCount = 0; - using (ETracer.StartActivity("event-source.consumer.loop", ActivityKind.Server)) + using (ETracer.StartActivity("evt-src.sys.consumer.loop", ActivityKind.Server)) { while (!cancellationToken.IsCancellationRequested) { @@ -388,14 +393,14 @@ IEnumerable ExtractTraceContext(Dictionary entri fullUri, async (cause) => { - Activity.Current?.AddEvent("event-source.consumer.event.ack", + Activity.Current?.AddEvent("evt-src.sys..consumer.event.ack", t => PrepareTrace(t).Add("cause", cause)); await AckAsync(result.Id); }, plan.Options.AckBehavior, logger, async (cause) => { - Activity.Current?.AddEvent("event-source.consumer.event.cancel", + Activity.Current?.AddEvent("evt-src.sys..consumer.event.cancel", t => PrepareTrace(t).Add("cause", cause)); batchCancellation.CancelSafe(); // cancel forward await CancelAsync(cancellableIds); @@ -432,7 +437,11 @@ IEnumerable ExtractTraceContext(Dictionary entri #endregion // var announcement = new Announcement(...) bool succeed; - using (var execActivity = ETracer.StartInternalTrace("event-source.consumer.execute-event")) + ConcumeEventsOperationCounter.WithEnvUriOperation(meta).Add(1); + Activity? execActivity = null; + if (ETracer.HasListeners()) + execActivity = ETracer.StartInternalTrace($"evt-src.consumer.{env}.{uri}.{meta.Operation.ToDash()}.invoke"); + using (execActivity) { succeed = await func(announcement, ack); execActivity?.SetTag("succeed", succeed); @@ -447,15 +456,10 @@ IEnumerable ExtractTraceContext(Dictionary entri // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) { - using (ETracer.StartInternalTrace("event-source.consumer.release-events-on-failure")) + using (ETracer.StartInternalTrace("evt-src.sys.consumer.release-events-on-failure")) { RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); - await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet - //using (ETracer.StartInternalTrace("event-source.consumer.delay", t => t.Add("duration", releaseDelay))) - //{ - // // let other potential consumer the chance of getting ownership - // await Task.Delay(releaseDelay, ct); - //} + await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet } } } @@ -488,7 +492,7 @@ async Task ReadBatchAsync() { isFirstBatchOrFailure = false; string group = plan.ConsumerGroup; - using var activity = ETracer.StartInternalTrace("event-source.consumer.read-batch", + using var activity = ETracer.StartInternalTrace("evt-src.sys.consumer.read-batch", t => PrepareTrace(t) .Add("consumer-group", group)); @@ -507,7 +511,7 @@ async Task ReadBatchAsync() .WithCancellation(cancellationToken, () => Array.Empty()); PrepareMeter(ConcumeBatchCountCounter).Add(1); activity?.SetTag("count", values.Length); - PrepareMeter(ConcumeBatchSumCounter).Add(values.Length); + PrepareMeter(ConcumeEventsCounter).Add(values.Length); } #region Exception Handling @@ -565,7 +569,7 @@ async Task ReadSelfPending() if (!isFirstBatchOrFailure) return values; - using var _ = ETracer.StartInternalTrace("event-source.consumer.self-pending"); + using var _ = ETracer.StartInternalTrace("evt-src.sys.consumer.self-pending"); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); @@ -625,12 +629,12 @@ async Task ClaimStaleMessages( if (values.Length != 0) return values; if (emptyBatchCount < claimingTrigger.EmptyBatchCount) return values; - using var _ = ETracer.StartInternalTrace("event-source.consumer.stale-events"); + using var _ = ETracer.StartInternalTrace("evt-src.sys.consumer.stale-events"); try { IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); StreamPendingInfo pendingInfo; - using (ETracer.StartInternalTrace("event-source.consumer.events-stealing.pending")) + using (ETracer.StartInternalTrace("evt-src.sys.consumer.events-stealing.pending")) { pendingInfo = await db.StreamPendingAsync(fullUri, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); } @@ -642,7 +646,7 @@ async Task ClaimStaleMessages( try { StreamPendingMessageInfo[] pendMsgInfo; - using (ETracer.StartInternalTrace("event-source.consumer.events-stealing.pending-events", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.events-stealing.pending-events", t => PrepareTrace(t).Add("from-consumer", c.Name))) { pendMsgInfo = await db.StreamPendingMessagesAsync( @@ -670,7 +674,7 @@ async Task ClaimStaleMessages( PrepareMeter(StealAmountCounter).WithTag("from-consumer", c.Name) .Add(count); // will claim events only if older than _setting.ClaimingTrigger.MinIdleTime - using (ETracer.StartInternalTrace("event-source.consumer.events-stealing.claim", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.events-stealing.claim", t => PrepareTrace(t) .Add("from-consumer", c.Name) .Add("message-count", count))) @@ -784,17 +788,17 @@ async Task ReleaseAsync(RedisValue[] freeTargets) IDatabaseAsync db = conn.GetDatabase(); try { - using (ETracer.StartInternalTrace("event-source.consumer.release-ownership", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.release-ownership", t => PrepareTrace(t).Add("consumer-group", plan.ConsumerGroup))) { - await db.StreamClaimAsync(plan.FullUri(), + await db.StreamClaimAsync(fullUri, plan.ConsumerGroup, RedisChannelConstants.NONE_CONSUMER, 1, freeTargets, flags: CommandFlags.DemandMaster); } - using (ETracer.StartInternalTrace("event-source.consumer.release.delay", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.release.delay", t => PrepareTrace(t).Add("delay", releaseDelay))) { // let other potential consumer the chance of getting ownership @@ -823,8 +827,11 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( #endregion // ReleaseAsync - ITagAddition PrepareTrace(ITagAddition t) => t.Add("uri", uri).Add("env", env); - ICounterBuilder PrepareMeter(Counter t) => t.WithTag("uri", uri).WithTag("env", env); + ITagAddition PrepareTrace(ITagAddition t) => t.Add("uri", uri).Add("env", env) + .Add("group-name", consumerGroup); + ICounterBuilder PrepareMeter(Counter t) => t.WithTag("uri", uri) + .WithTag("env", env) + .WithTag("group-name", consumerGroup); } #endregion // SubsribeToSingleAsync @@ -903,7 +910,7 @@ string GetMeta(string propKey) async Task FindAsync(EventKey entryId) { string lookForId = (string)entryId; - string key = plan.FullUri(); + string fullUri = plan.FullUri(); string originId = lookForId; int len = originId.IndexOf('-'); @@ -915,12 +922,12 @@ async Task FindAsync(EventKey entryId) { iteration++; StreamEntry[] entries = await db.StreamReadAsync( - key, + fullUri, startPosition, READ_BY_ID_CHUNK_SIZE, CommandFlags.DemandMaster); if (entries.Length == 0) - throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return nothing, start at ({startPosition}, iteration = {iteration})."); + throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{fullUri}] return nothing, start at ({startPosition}, iteration = {iteration})."); string k = string.Empty; foreach (StreamEntry e in entries) { @@ -938,11 +945,11 @@ async Task FindAsync(EventKey entryId) return e; } if (ePrefix != fromPrefix) - throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return not exists."); + throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{fullUri}] return not exists."); } startPosition = k; // next batch will start from last entry } - throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{key}] return not found."); + throw new KeyNotFoundException($"{mtdName} of [{lookForId}] from [{fullUri}] return not found."); } #endregion // FindAsync @@ -1026,7 +1033,7 @@ async IAsyncEnumerable IConsumerChannelProvider.GetAsyncEnumerable async IAsyncEnumerable AsyncLoop() { - string uri = plan.FullUri(); + string fullUri = plan.FullUri(); int iteration = 0; RedisValue startPosition = options?.From ?? BEGIN_OF_STREAM; @@ -1037,7 +1044,7 @@ async IAsyncEnumerable AsyncLoop() iteration++; StreamEntry[] entries = await db.StreamReadAsync( - uri, + fullUri, startPosition, READ_BY_ID_CHUNK_SIZE, CommandFlags.DemandMaster); @@ -1092,7 +1099,7 @@ private async ValueTask GetBucketAsync( { foreach (var strategy in strategies) { - using (ETracer.StartInternalTrace($"event-source.consumer.{strategy.Name}-storage.{storageType}.get")) + using (ETracer.StartInternalTrace($"evt-src.sys.consumer.{strategy.Name}-storage.{storageType}.get")) { bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } @@ -1100,7 +1107,7 @@ private async ValueTask GetBucketAsync( } else { - using (ETracer.StartInternalTrace($"event-source.consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) + using (ETracer.StartInternalTrace($"evt-src.sys.consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) { bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } @@ -1126,7 +1133,7 @@ private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationTo var newDelay = cfg.CalcNextDelay(previousDelay, cfg); var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); newDelay = TimeSpan.FromMilliseconds(limitDelay); - using (ETracer.StartInternalTrace("event-source.consumer.delay.when-empty-queue", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.delay.when-empty-queue", t => t.Add("delay", newDelay))) { await Task.Delay(newDelay, cancellationToken); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs deleted file mode 100644 index c25da270..00000000 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/ProducerChannelConstants.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace EventSourcing.Backbone.Channels; - -/// -/// Constants -/// -internal static class ProducerChannelConstants -{ - /// - /// The name of redis producer channel source - /// - public const string REDIS_CHANNEL_SOURCE = "event-source-redis-producer-channel"; -} diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index e3884c0e..996c0d0f 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -1,5 +1,7 @@ -using System.Collections.Immutable; +using System; +using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.Metrics; using System.Text.Json; using EventSourcing.Backbone.Producers; @@ -26,6 +28,9 @@ internal class RedisProducerChannel : IProducerChannelProvider private readonly IProducerStorageStrategy _defaultStorageStrategy; private const string META_SLOT = "____"; + private static readonly Counter ProduceEventsCounter = EMeter.CreateCounter("evt-src.sys.produce.events", "count", + "Sum of total produced events (messages)"); + #region Ctor /// @@ -66,8 +71,11 @@ public async ValueTask SendAsync( { Metadata meta = payload.Metadata; string id = meta.MessageId; - using var activity = ETracer.StartInternalTrace($"event-source.producer.{meta.Operation}.process", - t => t.Add("env", meta.Environment) + string env = meta.Environment.ToDash(); + string uri = meta.UriDash; + using var activity = ETracer.StartInternalTrace($"evt-src.producer.{meta.Operation}.process", + t => t.Add("env", env) + .Add("uri", uri) .Add("message-id", id)); #region var entries = new NameValueEntry[]{...} @@ -107,8 +115,9 @@ async Task LocalStreamAddAsync() { IConnectionMultiplexer conn = await _connFactory.GetAsync(CancellationToken.None); IDatabaseAsync db = conn.GetDatabase(); - using var scope = SuppressInstrumentationScope.Begin(); + // using var scope = SuppressInstrumentationScope.Begin(); var k = meta.FullUri(); + ProduceEventsCounter.WithTag("uri", uri).WithTag("env", env).Add(1); var result = await db.StreamAddAsync(k, entries, flags: CommandFlags.DemandMaster); return result; @@ -117,14 +126,14 @@ async Task LocalStreamAddAsync() catch (RedisConnectionException ex) { - _logger.LogError(ex, "REDIS Connection Failure: push event [{id}] into the [{URI}] stream: {operation}", - meta.MessageId, meta.Uri, meta.Operation); + _logger.LogError(ex, "REDIS Connection Failure: push event [{id}] into the [{env}:{URI}] stream: {operation}", + meta.MessageId, env, uri, meta.Operation); throw; } catch (Exception ex) { - _logger.LogError(ex, "Fail to push event [{id}] into the [{URI}] stream: {operation}", - meta.MessageId, meta.Uri, meta.Operation); + _logger.LogError(ex, "Fail to push event [{id}] into the [{env}:{URI}] stream: {operation}", + meta.MessageId, env, uri, meta.Operation); throw; } @@ -150,7 +159,7 @@ async ValueTask LocalStoreBucketAsync(EventBucketCategories storageType) async ValueTask SaveBucketAsync(IProducerStorageStrategy strategy) { - using (ETracer.StartInternalTrace($"event-source.producer.{strategy.Name}-storage.{storageType}.set")) + using (ETracer.StartInternalTrace($"evt-src.producer.{strategy.Name}-storage.{storageType}.set")) { IImmutableDictionary metaItems = await strategy.SaveBucketAsync(id, bucket, storageType, meta); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs index ba53fd1e..0ba27877 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisChannelConstants.cs @@ -3,7 +3,6 @@ public static class RedisChannelConstants { public const string CHANNEL_TYPE = "REDIS Channel V1"; - public const string REDIS_CHANNEL_SOURCE = "event-source-redis-channel"; public const string META_ARRAY_SEPARATOR = "~|~"; public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 0d424e49..4ea67c3a 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -18,9 +18,9 @@ public static class RedisCommonProviderExtensions private const int MIN_DELAY = 2; private const int SPIN_LIMIT = 30; private const int MAX_DELAY = 3_000; - private static Counter KeyMissingCounter = EMeter.CreateCounter("event-source.key-missing", "count", "count missing key events"); - private static Counter CreateConsumerGroupCounter = EMeter.CreateCounter("event-source.create-consumer-group", "count", "creating a consumer group"); - private static Counter CreateConsumerGroupRetryCounter = EMeter.CreateCounter("event-source.create-consumer-group-retry", "count", "retries of creating a consumer group"); + private static Counter KeyMissingCounter = EMeter.CreateCounter("evt-src.sys.key-missing", "count", "count missing key events"); + private static Counter CreateConsumerGroupCounter = EMeter.CreateCounter("evt-src.sys.create-consumer-group", "count", "creating a consumer group"); + private static Counter CreateConsumerGroupRetryCounter = EMeter.CreateCounter("evt-src.sys.create-consumer-group-retry", "count", "retries of creating a consumer group"); private static readonly AsyncLock _lock = new AsyncLock(TimeSpan.FromSeconds(20)); @@ -30,8 +30,7 @@ public static class RedisCommonProviderExtensions /// Creates the consumer group if not exists asynchronous. /// /// The connection factory. - /// The environment. - /// The event source URI. + /// The plan. /// The consumer group. /// The logger. /// The cancellation token. @@ -44,11 +43,11 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( CancellationToken cancellationToken) { Env env = plan.Environment; - string uri = plan.Uri; + string uri = plan.UriDash; string fullUri = plan.FullUri(); StreamGroupInfo[] groupsInfo = Array.Empty(); - using var track = ETracer.StartInternalTrace("event-source.consumer.create-consumer-group", + using var track = ETracer.StartInternalTrace("evt-src.sys.consumer.create-consumer-group", t => PrepareTrace(t)); PrepareMeter(CreateConsumerGroupCounter).Add(1); @@ -72,7 +71,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( if (tryNumber > SPIN_LIMIT) { delay = Math.Min(delay * 2, MAX_DELAY); - using (ETracer.StartInternalTrace("event-source.consumer.delay.key-not-exists", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.delay.key-not-exists", t => PrepareTrace(t) .Add("delay", delay) .Add("try-number", tryNumber))) @@ -101,7 +100,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( #endregion // Validation (if key exists) - using (ETracer.StartInternalTrace("event-source.consumer.get-consumer-group-info", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.get-consumer-group-info", t => PrepareTrace(t) .Add("try-number", tryNumber))) { @@ -123,7 +122,6 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( } else { - //await Task.Delay(KEY_NOT_EXISTS_DELAY); logger.LogDebug(ex, "Create Consumer Group If Not Exists: failed. {info}", CurrentInfo()); } } @@ -145,7 +143,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { try { - using (ETracer.StartInternalTrace("event-source.consumer.create-consumer-group", + using (ETracer.StartInternalTrace("evt-src.sys.consumer.create-consumer-group", t => PrepareTrace(t) .Add("try-number", tryNumber))) { @@ -212,8 +210,12 @@ string CurrentInfo() => @$" } - ITagAddition PrepareTrace(ITagAddition t) => t.Add("uri", uri).Add("env", env).Add("group-name", consumerGroup); - ICounterBuilder PrepareMeter(Counter t) => t.WithTag("uri", uri).WithTag("env", env); + ITagAddition PrepareTrace(ITagAddition t) => t.Add("uri", uri) + .Add("env", env) + .Add("group-name", consumerGroup); + ICounterBuilder PrepareMeter(Counter t) => t.WithTag("uri", uri) + .WithTag("env", env) + .WithTag("group-name", consumerGroup); } #endregion // CreateConsumerGroupIfNotExistsAsync diff --git a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs index 35765745..9eacd789 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/Builder/ConsumerPlan.cs @@ -172,12 +172,30 @@ IConsumerChannelProvider IConsumerPlan.Channel #region Uri + private string _uri = string.Empty; /// - /// The stream's key (identity) + /// The stream identifier (the URI combined with the environment separate one stream from another) /// - public string Uri { get; } = string.Empty; + public string Uri + { + get => _uri; + init + { + _uri = value; + UriDash = value.ToDash(); + } + } + + #endregion Uri + + #region UriDash + + /// + /// Gets a lower-case URI with dash separator. + /// + public string UriDash { get; private set; } = string.Empty; - #endregion // Uri + #endregion // UriDash #region Options diff --git a/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs b/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs index ebf7f4d7..0a44eb51 100644 --- a/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs +++ b/Consumers/EventSourcing.Backbone.Consumers/ConsumerTelemetryExtensions.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Diagnostics; +using System.Diagnostics.Metrics; namespace EventSourcing.Backbone.Consumers; @@ -22,7 +23,7 @@ public static class ConsumerTelemetryExtensions { // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - var activityName = $"event-source.Consumer.{meta.Operation}.process"; + var activityName = $"evt-src.sys.Consumer.{meta.Environment.ToDash()}.{meta.UriDash}.{meta.Operation}.process"; var tags = new ActivityTagsCollection(); var t = new TagAddition(tags); @@ -38,4 +39,59 @@ public static class ConsumerTelemetryExtensions } #endregion // StartConsumerTrace + + #region WithEnvUriOperation + + /// + /// Add URI, Environment and Operation labels. + /// + /// + /// The counter. + /// The metadata. + /// + public static ICounterBuilder WithEnvUriOperation(this Counter counter, Metadata metadata) + where T : struct + { + return counter + .WithTag("uri", metadata.UriDash) + .WithTag("env", metadata.Environment.DashFormat()) + .WithTag("operation", metadata.Operation.ToDash()); + } + + /// + /// Add URI, Environment and Operation labels. + /// + /// + /// The counter. + /// The metadata. + /// + public static ICounterBuilder WithEnvUriOperation(this ICounterBuilder counter, Metadata metadata) + where T : struct + { + return counter + .WithTag("uri", metadata.UriDash) + .WithTag("env", metadata.Environment.DashFormat()) + .WithTag("operation", metadata.Operation.ToDash()); + } + + #endregion // WithEnvUriOperation + + #region AddEnvUriOperation + + /// + /// Add URI, Environment and Operation labels. + /// + /// + /// The counter. + /// The metadata. + /// + public static ITagAddition AddEnvUriOperation(this ITagAddition counter, Metadata metadata) + { + return counter + .Add("uri", metadata.UriDash) + .Add("env", metadata.Environment.DashFormat()) + .Add("operation", metadata.Operation.ToDash()); + } + + #endregion // AddEnvUriOperation } diff --git a/Directory.Build.props b/Directory.Build.props index 9e553924..364fb105 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,7 +22,7 @@ true - event-source, pub, sub, pubsub, messaging, reliable, redis, bnaya + event-source, event-driven, event-sourcing, producer, consumer, pub, sub, pub-sub, messaging, reliable, redis, bnaya https://medium.com/@bnayae https://github.com/bnayae/Event-Source-Backbone GitHub diff --git a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs index 4094f1ea..5c695b0a 100644 --- a/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs +++ b/EventSourcing.Backbone.Abstractions/Consumer/Ack/AckOnce.cs @@ -21,9 +21,9 @@ public class AckOnce : IAck private readonly AckBehavior _behavior; private readonly ILogger _logger; private int _ackCount = 0; - private static readonly Counter AckCounter = EMeter.CreateCounter("event-source.consumer.event.ack", "count", + private static readonly Counter AckCounter = EMeter.CreateCounter("evt-src.sys.consumer.event.ack", "count", "Event's message handling acknowledge count"); - private static readonly Counter AbortCounter = EMeter.CreateCounter("event-source.consumer.event.abort", "count", + private static readonly Counter AbortCounter = EMeter.CreateCounter("evt-src.sys.consumer.event.abort", "count", "Event's message handling aborted (cancel) count"); #region Ctor diff --git a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs index 2ecd9f97..6ecae2f7 100644 --- a/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs +++ b/EventSourcing.Backbone.Abstractions/Entities/Announcement/Metadata.cs @@ -65,13 +65,31 @@ public record Metadata #region Uri + private string _uri = string.Empty; /// /// The stream identifier (the URI combined with the environment separate one stream from another) /// - public required string Uri { get; init; } + public required string Uri + { + get => _uri; + init + { + _uri = value; + UriDash = value.ToDash(); + } + } #endregion Uri + #region UriDash + + /// + /// Gets a lower-case URI with dash separator. + /// + public string UriDash { get; private set; } = string.Empty; + + #endregion // UriDash + #region Origin /// diff --git a/EventSourcing.Backbone.Abstractions/Env.cs b/EventSourcing.Backbone.Abstractions/Env.cs index 8e58339b..5918912d 100644 --- a/EventSourcing.Backbone.Abstractions/Env.cs +++ b/EventSourcing.Backbone.Abstractions/Env.cs @@ -12,6 +12,9 @@ namespace EventSourcing.Backbone public sealed class Env : IEquatable { private readonly string _value; + private string? _dash; + private string? _format; + private string? _dashFormat; #region Ctor @@ -58,13 +61,33 @@ public Env(string value) #endregion // Cast overloads + #region DashFormat + + /// + /// Formats the specified environment into convention + dash. + /// + /// + public string DashFormat() + { + if (string.IsNullOrEmpty(_dashFormat)) + _dashFormat = Format().ToDash(); + return _dashFormat; + } + + #endregion // DashFormat + #region Format /// /// Formats the specified environment into convention. /// /// - public string Format() => Env.Format(_value); + public string Format() + { + if (string.IsNullOrEmpty(_format)) + _format = Env.Format(_value); + return _format; + } /// /// Formats the specified environment into convention. @@ -84,6 +107,20 @@ private static string Format(string e) #endregion // Format + #region ToDash + + /// + /// Converts to dash-string. + /// + public string ToDash() + { + if (string.IsNullOrEmpty(_dash)) + _dash = _value.ToDash(); + return _dash; + } + + #endregion // ToDash + #region ToString /// diff --git a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs index faccf71f..2d4c68a6 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceConstants.cs @@ -24,6 +24,6 @@ public static class EventSourceConstants } }; - public const string TELEMETRY_SOURCE = "event-source"; + public const string TELEMETRY_SOURCE = "evt-src"; } diff --git a/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs b/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs index eb3d16f5..cc88c2b8 100644 --- a/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs +++ b/EventSourcing.Backbone.Abstractions/EventSourceTelemetry.cs @@ -7,7 +7,4 @@ public class EventSourceTelemetry { public readonly static Meter EMeter = new(EventSourceConstants.TELEMETRY_SOURCE); public static readonly ActivitySource ETracer = new ActivitySource(EventSourceConstants.TELEMETRY_SOURCE); - - //private static readonly Counter DelayCounter = Metics.CreateCounter("event-source.consumer.delay", "", - // ""); } diff --git a/EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs b/EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs index cb310adf..12b53b0f 100644 --- a/EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs +++ b/EventSourcing.Backbone.Abstractions/Interfaces/IPlanRoute.cs @@ -1,17 +1,20 @@ -namespace EventSourcing.Backbone +namespace EventSourcing.Backbone; + +/// +/// Plan routing identification +/// +public interface IPlanRoute { /// - /// Plan routing identification + /// Environment (part of the stream key). /// - public interface IPlanRoute - { - /// - /// Environment (part of the stream key). - /// - Env Environment { get; } - /// - /// The stream identifier (the URI combined with the environment separate one stream from another) - /// - string Uri { get; } - } + Env Environment { get; } + /// + /// The stream identifier (the URI combined with the environment separate one stream from another) + /// + string Uri { get; } + /// + /// The stream identifier (the URI combined with the environment separate one stream from another) formatted with lower-case and dash separator + /// + string UriDash { get; } } \ No newline at end of file diff --git a/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs index 06bca15e..a525eaec 100644 --- a/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs +++ b/EventSourcing.Backbone.Abstractions/Producer/Plan/ProducerPlan.cs @@ -150,14 +150,32 @@ IProducerChannelProvider IProducerPlan.Channel #endregion // Environment - #region Key + #region Uri + private string _uri = string.Empty; /// - /// The stream key + /// The stream identifier (the URI combined with the environment separate one stream from another) /// - public string Uri { get; } = string.Empty; + public string Uri + { + get => _uri; + init + { + _uri = value; + UriDash = value.ToDash(); + } + } + + #endregion Uri + + #region UriDash + + /// + /// Gets a lower-case URI with dash separator. + /// + public string UriDash { get; private set; } = string.Empty; - #endregion // Key + #endregion // UriDash #region Options diff --git a/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs b/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs index f425606f..3005a0f0 100644 --- a/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs +++ b/EventSourcing.Backbone.Abstractions/Telemetry/EventSourceTelemetryExtensions.cs @@ -79,11 +79,11 @@ public static void InjectTelemetryTags(this Metadata meta, Activity? activity) // See: // * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#messaging-attributes - activity?.SetTag("event-source.env", meta.Environment); - activity?.SetTag("event-source.uri", meta.Uri); - activity?.SetTag("event-source.operation", meta.Operation); - activity?.SetTag("event-source.message-id", meta.MessageId); - activity?.SetTag("event-source.channel-type", meta.ChannelType); + activity?.SetTag("evt-src.env", meta.Environment); + activity?.SetTag("evt-src.uri", meta.Uri); + activity?.SetTag("evt-src.operation", meta.Operation); + activity?.SetTag("evt-src.message-id", meta.MessageId); + activity?.SetTag("evt-src.channel-type", meta.ChannelType); } #endregion // InjectTelemetryTags diff --git a/EventSourcing.Backbone.sln b/EventSourcing.Backbone.sln index 20396ca7..f913f4da 100644 --- a/EventSourcing.Backbone.sln +++ b/EventSourcing.Backbone.sln @@ -222,7 +222,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {C1618305-4F0B-4AAA-8386-CA31DCBC5F59} = {4DE45B98-D18F-4E1D-9D2F-A4E40C11BC1F} - {EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2} = {2C4E653D-8B7A-48BA-A9DB-F9707BC04380} {6821E250-3BE8-4CDA-AFEA-FB8C9B4C4106} = {EC34CEBD-EEDE-4CBA-A28A-A71BCB51D5D2} {90B4EBE9-7CDB-42E0-B5E2-9FCAC4AD52CA} = {3FBE7FA1-700D-4B58-8569-15F533F761D1} {64FC6860-BD4C-4C11-BF6E-E99BB4D91723} = {75CE8A6F-D63B-491D-8834-8D3BA2127208} diff --git a/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs b/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs index 243df6e6..0994fcf4 100644 --- a/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs +++ b/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs @@ -20,11 +20,15 @@ public static class ProducerTelemetryExtensions // Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name + if(!activitySource.HasListeners()) + return null; + var tags = new ActivityTagsCollection(); var t = new TagAddition(tags); tagsAction?.Invoke(t); - var activityName = $"event-source.producer.{meta.Operation}.send"; + + var activityName = activitySource.HasListeners() ? $"evt-src.producer.{meta.Environment.ToDash()}.{meta.UriDash}.{meta.Operation.ToDash()}.send" : string.Empty; Activity? activity = activitySource.StartActivity(activityName, ActivityKind.Producer); meta.InjectTelemetryTags(activity); diff --git a/Tests/ConsoleTest/Constants.cs b/Tests/ConsoleTest/Constants.cs index 7b0907a2..830bf968 100644 --- a/Tests/ConsoleTest/Constants.cs +++ b/Tests/ConsoleTest/Constants.cs @@ -4,7 +4,7 @@ namespace ConsoleTest; internal static class Constants { - public const int MAX = 100; //3_000; + public const int MAX = 1_000; public const string END_POINT_KEY = "REDIS_EVENT_SOURCE_ENDPOINT"; public const string ENV = $"console-test"; public static readonly IFooConsumer Subscriber = A.Fake(); diff --git a/Tests/ConsoleTest/Program.cs b/Tests/ConsoleTest/Program.cs index 1df0c02d..abe44438 100644 --- a/Tests/ConsoleTest/Program.cs +++ b/Tests/ConsoleTest/Program.cs @@ -85,9 +85,9 @@ static async Task Cleanup(string END_POINT_KEY, string URI, ILogger fakeLogger) IDatabaseAsync db = conn.GetDatabase(); var ab = new ActionBlock(k => db.KeyDeleteAsync(k, CommandFlags.DemandMaster), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 30 }); - foreach (string key in keys) + foreach (string? key in keys) { - ab.Post(key); + ab.Post(key!); } ab.Complete(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs index cd6e461f..0adaeb90 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/AckPatternsTests.cs @@ -17,7 +17,7 @@ #pragma warning disable S3881 // "IDisposable" should be implemented correctly #pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { @@ -186,10 +186,10 @@ public async Task OnSucceed_ACK_WithFailure_Test() #endregion // OnSucceed_ACK_WithFailure_Test - #region OnFinaly_ACK_WithFailure_Test + #region OnFinally_ACK_WithFailure_Test [Fact(Timeout = TIMEOUT)] - public async Task OnFinaly_ACK_WithFailure_Test() + public async Task OnFinally_ACK_WithFailure_Test() { #region ISequenceOperations producer = ... @@ -252,11 +252,12 @@ public async Task OnFinaly_ACK_WithFailure_Test() #endregion // Validation } - #endregion // OnFinaly_ACK_WithFailure_Test + #endregion // OnFinally_ACK_WithFailure_Test #region Manual_ACK_Test [Fact(Timeout = TIMEOUT)] + [Trait("Profile", "true")] public async Task Manual_ACK_Test() { #region ISequenceOperations producer = ... @@ -281,8 +282,6 @@ public async Task Manual_ACK_Test() if (Interlocked.Increment(ref tryNumber) < 5) throw new ApplicationException("test intensional exception"); await meta.AckAsync(); - //ConsumerMetadata meta = ConsumerMetadata.Context; - //await Ack.Current.AckAsync(); }); A.CallTo(() => _subscriber.EarseAsync(A.Ignored, A.Ignored)) .ReturnsLazily(() => Ack.Current.AckAsync()); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs index d0bcd81f..0d0a5bb5 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndExplicitTests.cs @@ -15,7 +15,7 @@ #pragma warning disable S3881 // "IDisposable" should be implemented correctly -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest // TODO: [bnaya 2020-10] ensure message order(cancel ack should cancel all following messages) diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs index 57e905f2..4c4cda65 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndStressTests.cs @@ -13,7 +13,7 @@ #pragma warning disable S3881 // "IDisposable" should be implemented correctly -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs index 07a7a49f..ba2092c3 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EndToEndTests.cs @@ -19,7 +19,7 @@ #pragma warning disable S3881 // "IDisposable" should be implemented correctly -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { @@ -197,10 +197,16 @@ public async Task PartialConsumer_Strict_Succeed_Test() #endregion // ISequenceOperations producer = ... + var sw = Stopwatch.StartNew(); + var p = new Person(100, "bnaya"); await producer.Stage1Async(p, "ABC"); await producer.Stage2Async(p.ToJson(), "ABC".ToJson()); + var snapshot = sw.Elapsed; + _outputHelper.WriteLine($"Produce = {snapshot:mm\\:ss\\.ff}"); + snapshot = sw.Elapsed; + CancellationToken cancellation = GetCancellationToken(); #region await using IConsumerLifetime subscription = ...Subscribe(...) @@ -220,6 +226,9 @@ public async Task PartialConsumer_Strict_Succeed_Test() await subscription.Completion; + snapshot = sw.Elapsed - snapshot; + _outputHelper.WriteLine($"Consumed = {snapshot:mm\\:ss\\.ff}"); + #region Validation A.CallTo(_fakeLogger).Where(call => call.Method.Name == "Log" && call.GetArgument(0) == LogLevel.Critical).MustNotHaveHappened(); diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs index e8a99efc..d40d8a37 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/HelloWorld/HelloWorldTests.cs @@ -6,7 +6,7 @@ using Xunit; using Xunit.Abstractions; -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.IntegrationTests.HelloWorld { diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs index 8e3c7898..1bb657a1 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/InheritanceTests.cs @@ -15,7 +15,7 @@ using Xunit; using Xunit.Abstractions; -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs index 4dcf841b..e49b2fe3 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationReceiverTest.cs @@ -9,7 +9,7 @@ using Xunit; using Xunit.Abstractions; -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { @@ -43,7 +43,7 @@ public MigrationReceiverTest(ITestOutputHelper outputHelper) .AddVoidStrategy(); _sourceConsumerBuilder = ConsumerBuilder.Empty.UseRedisChannel(credentialsKeys: new RedisCredentialsEnvKeys { Endpoint = SOURCE_KEY }) - .AddS3Storage(new S3Options { Bucket = "event-source-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); + .AddS3Storage(new S3Options { Bucket = "evt-src-storage", EnvironmentConvension = S3EnvironmentConvention.BucketPrefix }); } #endregion // Ctor diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs index 8debe44e..5549ed64 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/MigrationTest.cs @@ -12,7 +12,7 @@ using Xunit; using Xunit.Abstractions; -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs index 0ddace58..ea261fa5 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs +++ b/Tests/EventSourcing.Backbone.IntegrationTests/TestsBase.cs @@ -14,7 +14,7 @@ #pragma warning disable S3881 // "IDisposable" should be implemented correctly -// docker run -p 6379:6379 -it --rm --name redis-event-source redislabs/rejson:latest +// docker run -p 6379:6379 -it --rm --name redis-evt-src redislabs/rejson:latest namespace EventSourcing.Backbone.Tests { diff --git a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs index f5c43951..af2dba5d 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/AspCoreExtensions.cs @@ -23,13 +23,9 @@ public static class AspCoreExtensions /// Adds the standard configuration. /// /// The services. - /// The host env. - /// /// public static IConnectionMultiplexer AddRedis( - this IServiceCollection services, - IHostEnvironment hostEnv, - string shortAppName) + this IServiceCollection services) { IConnectionMultiplexer redisConnection = RedisClientFactory.CreateProviderAsync().Result; services.AddSingleton(redisConnection); @@ -60,6 +56,13 @@ public static IMvcBuilder WithJsonOptions( #endregion // WithJsonOptions + #region WithDefault + + /// + /// Withes the default. + /// + /// The options. + /// public static JsonSerializerOptions WithDefault(this JsonSerializerOptions options) { options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; @@ -70,49 +73,6 @@ public static JsonSerializerOptions WithDefault(this JsonSerializerOptions optio return options; } - #region UseRestDefaults - - /// - /// Pre-configured host defaults for rest API. - /// - /// The type of the startup. - /// The builder. - /// The process arguments. - /// - public static IHostBuilder UseRestDefaults( - this IHostBuilder builder, - params string[] args) where TStartup : class - { - builder.ConfigureHostConfiguration(cfg => - { - // https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.1&tabs=windows - // cfg.AddEnvironmentVariables() - // cfg.AddJsonFile() - // cfg.AddInMemoryCollection() - // cfg.AddConfiguration() - // cfg.AddUserSecrets() - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureKestrel( - (WebHostBuilderContext context, - KestrelServerOptions options) => - { - options.ConfigureEndpointDefaults(c => c.Protocols = HttpProtocols.Http1AndHttp2); - }); - webBuilder.UseStartup(); - }).ConfigureLogging((context, builder) => - { - builder.AddOpenTelemetry(options => - { - options.IncludeScopes = true; - options.ParseStateValues = true; - options.IncludeFormattedMessage = true; - }); - }); - return builder; - } - - #endregion // UseRestDefaults + #endregion // WithDefault } } diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs index 0b589877..3559b012 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Jobs/MigrationJob.cs @@ -48,7 +48,7 @@ public MigrationJob( /// The cancellation token. protected override async Task OnStartAsync(CancellationToken cancellationToken) { - SubscriptionBridge subscription = new(_forwarder, _client); + SubscriptionBridge subscription = new(_forwarder); _builder.Group("Demo-Migration-GROUP") .Subscribe(subscription); @@ -73,12 +73,10 @@ public void Dispose() private class SubscriptionBridge : ISubscriptionBridge { private readonly IEventsMigration _fw; - private readonly HttpClient _client; - public SubscriptionBridge(IEventsMigration fw, HttpClient client) + public SubscriptionBridge(IEventsMigration fw) { _fw = fw; - _client = client; } public async Task BridgeAsync(Announcement announcement, IConsumerBridge consumerBridge) diff --git a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs index 2295c943..6094bd5d 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs @@ -34,10 +34,10 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati cfg //.AddSource("StackExchange.Redis") //.AddSource("StackExchangeRedisConnectionInstrumentation") - //.AddRedisInstrumentation(RedisClientFactory.CreateProviderAsync().Result, c => - //{ - // c.FlushInterval = TimeSpan.FromSeconds(1); - //}) + .AddRedisInstrumentation(RedisClientFactory.CreateProviderAsync().Result, c => + { + c.FlushInterval = TimeSpan.FromSeconds(5); + }) //.ConfigureRedisInstrumentation((provider, b) => { // var conn = provider.GetRequiredService(); // b.AddConnection(conn); diff --git a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs index 5cd0a41a..edea79e7 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/Program.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/Program.cs @@ -71,7 +71,7 @@ c.DefaultRequestHeaders.Add("wk-pattern", "migration"); }); -//IConnectionMultiplexer redisConnection = services.AddRedis(environment, shortAppName); +//IConnectionMultiplexer redisConnection = services.AddRedis(); builder.AddOpenTelemetryEventSourcing(); //services.AddOpenTelemetry(environment, shortAppName, redisConnection); From 3d820cd359a567329d2af20d097bd11c895bf09d Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 25 Jun 2023 11:51:15 +0300 Subject: [PATCH 175/178] bug: dispose fix crash --- .../EventSourceRedisConnectionFactory.cs | 30 ++++++++++++++----- .../Class1.cs | 22 -------------- ...tSourcing.Backbone.IntegrationTests.csproj | 1 - .../xunit.runner.json | 4 ++- 4 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs index 32e267f2..c7b2df9b 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/Factory/EventSourceRedisConnectionFactory.cs @@ -223,14 +223,22 @@ async Task IEventSourceRedisConnectionFactory.GetDatabaseAsync(C /// private void Dispose(bool disposing) { - _logger.LogWarning("REDIS [{kind}]: Disposing connection", Kind); - if (!Disposed) + try { - var conn = _redisTask.Result; - conn.Dispose(); - Disposed = true; - OnDispose(disposing); + _logger.LogWarning("REDIS [{kind}]: Disposing connection", Kind); } + catch { } + try + { + if (!Disposed) + { + var conn = _redisTask.Result; + conn.Dispose(); + Disposed = true; + OnDispose(disposing); + } + } + catch { } } /// @@ -238,9 +246,9 @@ private void Dispose(bool disposing) /// void IDisposable.Dispose() { + GC.SuppressFinalize(this); // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); - GC.SuppressFinalize(this); } /// @@ -256,9 +264,15 @@ protected virtual void OnDispose(bool disposing) { } /// public async ValueTask DisposeAsync() { - _logger.LogWarning("REDIS [{kind}]: Disposing connection (async)", Kind); + GC.SuppressFinalize(this); + try + { + _logger.LogWarning("REDIS [{kind}]: Disposing connection (async)", Kind); + } + catch { } var redis = await _redisTask; redis.Dispose(); + OnDispose(true); } /// diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs b/Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs deleted file mode 100644 index 184b269d..00000000 --- a/Tests/EventSourcing.Backbone.IntegrationTests/Class1.cs +++ /dev/null @@ -1,22 +0,0 @@ - -#nullable enable - -using System.CodeDom.Compiler; - -namespace EventSourcing.Backbone.UnitTests.Entities -{ - /// - /// Entity mapper is responsible of mapping announcement to DTO generated from SequenceOperationsConsumer - /// - /// - [GeneratedCode("EventSourcing.Backbone.SrcGen", "1.1.141.0")] - public static class SequenceOperationsConsumerEntityMapperExtensions1 - { - /// - /// Specialize Enumerator of event produced by ISequenceOperationsConsumer - /// - public static IConsumerIterator SpecializeSequenceOperationsConsumer(IConsumerIterator iterator) => - iterator.Specialize(SequenceOperationsConsumerEntityMapper.Default); - } - -} diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj index fa72a0a0..86530467 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj +++ b/Tests/EventSourcing.Backbone.IntegrationTests/EventSourcing.Backbone.IntegrationTests.csproj @@ -71,5 +71,4 @@ - diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json b/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json index 13b949c6..0e4a6103 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json +++ b/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json @@ -1,4 +1,6 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - + "maxParallelThreads": 5, + "diagnosticMessages": true, + "longRunningTestSeconds": 30 } From c74c3ce81b891f2a5fe5b5bfbc8cdd9bb8140933 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 25 Jun 2023 12:08:02 +0300 Subject: [PATCH 176/178] test: config --- Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json b/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json index 0e4a6103..f5ac9bed 100644 --- a/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json +++ b/Tests/EventSourcing.Backbone.IntegrationTests/xunit.runner.json @@ -1,6 +1,6 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "maxParallelThreads": 5, + "maxParallelThreads": 20, "diagnosticMessages": true, "longRunningTestSeconds": 30 } From e8f9be894ff043257dfb534fcc6e2ae9614948e0 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 25 Jun 2023 15:02:36 +0300 Subject: [PATCH 177/178] rfc: tracing prefix --- .../RedisConsumerChannel.cs | 36 +++++++++---------- .../RedisProducerChannel.cs | 2 +- .../RedisCommonProviderExtensions.cs | 8 ++--- .../ProducerTelemetryExtensions.cs | 3 +- ...EventSourcing.Backbone.WebEventTest.csproj | 1 + .../OpenTelemetryExtensions.cs | 1 + 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs index 85f5540a..4c30881f 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisConsumerProvider/RedisConsumerChannel.cs @@ -256,15 +256,15 @@ await _connFactory.CreateConsumerGroupIfNotExistsAsync( TimeSpan delay = TimeSpan.Zero; int emptyBatchCount = 0; - using (ETracer.StartActivity("evt-src.sys.consumer.loop", ActivityKind.Server)) - { + //using (ETracer.StartActivity("consumer.loop", ActivityKind.Server)) + //{ while (!cancellationToken.IsCancellationRequested) { var proceed = await HandleBatchAsync(); if (!proceed) break; } - } + //} #region HandleBatchAsync @@ -393,14 +393,14 @@ IEnumerable ExtractTraceContext(Dictionary entri fullUri, async (cause) => { - Activity.Current?.AddEvent("evt-src.sys..consumer.event.ack", + Activity.Current?.AddEvent("consumer.event.ack", t => PrepareTrace(t).Add("cause", cause)); await AckAsync(result.Id); }, plan.Options.AckBehavior, logger, async (cause) => { - Activity.Current?.AddEvent("evt-src.sys..consumer.event.cancel", + Activity.Current?.AddEvent("consumer.event.cancel", t => PrepareTrace(t).Add("cause", cause)); batchCancellation.CancelSafe(); // cancel forward await CancelAsync(cancellableIds); @@ -440,7 +440,7 @@ IEnumerable ExtractTraceContext(Dictionary entri ConcumeEventsOperationCounter.WithEnvUriOperation(meta).Add(1); Activity? execActivity = null; if (ETracer.HasListeners()) - execActivity = ETracer.StartInternalTrace($"evt-src.consumer.{env}.{uri}.{meta.Operation.ToDash()}.invoke"); + execActivity = ETracer.StartInternalTrace($"{env}.{uri}.{meta.Operation.ToDash()}.invoke"); using (execActivity) { succeed = await func(announcement, ack); @@ -456,7 +456,7 @@ IEnumerable ExtractTraceContext(Dictionary entri // TODO: [bnaya 2023-06-19 #RELEASE] committed id should be captured if (options.PartialBehavior == Enums.PartialConsumerBehavior.Sequential) { - using (ETracer.StartInternalTrace("evt-src.sys.consumer.release-events-on-failure")) + using (ETracer.StartInternalTrace("consumer.release-events-on-failure")) { RedisValue[] freeTargets = results[i..].Select(m => m.Id).ToArray(); await ReleaseAsync(freeTargets); // release the rest of the batch which doesn't processed yet @@ -492,7 +492,7 @@ async Task ReadBatchAsync() { isFirstBatchOrFailure = false; string group = plan.ConsumerGroup; - using var activity = ETracer.StartInternalTrace("evt-src.sys.consumer.read-batch", + using var activity = ETracer.StartInternalTrace("consumer.read-batch", t => PrepareTrace(t) .Add("consumer-group", group)); @@ -569,7 +569,7 @@ async Task ReadSelfPending() if (!isFirstBatchOrFailure) return values; - using var _ = ETracer.StartInternalTrace("evt-src.sys.consumer.self-pending"); + using var _ = ETracer.StartInternalTrace("consumer.self-pending"); IConnectionMultiplexer conn = await _connFactory.GetAsync(cancellationToken); IDatabaseAsync db = conn.GetDatabase(); @@ -629,12 +629,12 @@ async Task ClaimStaleMessages( if (values.Length != 0) return values; if (emptyBatchCount < claimingTrigger.EmptyBatchCount) return values; - using var _ = ETracer.StartInternalTrace("evt-src.sys.consumer.stale-events"); + using var _ = ETracer.StartInternalTrace("consumer.stale-events"); try { IDatabaseAsync db = await _connFactory.GetDatabaseAsync(ct); StreamPendingInfo pendingInfo; - using (ETracer.StartInternalTrace("evt-src.sys.consumer.events-stealing.pending")) + using (ETracer.StartInternalTrace("consumer.events-stealing.pending")) { pendingInfo = await db.StreamPendingAsync(fullUri, plan.ConsumerGroup, flags: CommandFlags.DemandMaster); } @@ -646,7 +646,7 @@ async Task ClaimStaleMessages( try { StreamPendingMessageInfo[] pendMsgInfo; - using (ETracer.StartInternalTrace("evt-src.sys.consumer.events-stealing.pending-events", + using (ETracer.StartInternalTrace("consumer.events-stealing.pending-events", t => PrepareTrace(t).Add("from-consumer", c.Name))) { pendMsgInfo = await db.StreamPendingMessagesAsync( @@ -674,7 +674,7 @@ async Task ClaimStaleMessages( PrepareMeter(StealAmountCounter).WithTag("from-consumer", c.Name) .Add(count); // will claim events only if older than _setting.ClaimingTrigger.MinIdleTime - using (ETracer.StartInternalTrace("evt-src.sys.consumer.events-stealing.claim", + using (ETracer.StartInternalTrace("consumer.events-stealing.claim", t => PrepareTrace(t) .Add("from-consumer", c.Name) .Add("message-count", count))) @@ -788,7 +788,7 @@ async Task ReleaseAsync(RedisValue[] freeTargets) IDatabaseAsync db = conn.GetDatabase(); try { - using (ETracer.StartInternalTrace("evt-src.sys.consumer.release-ownership", + using (ETracer.StartInternalTrace("consumer.release-ownership", t => PrepareTrace(t).Add("consumer-group", plan.ConsumerGroup))) { await db.StreamClaimAsync(fullUri, @@ -798,7 +798,7 @@ await db.StreamClaimAsync(fullUri, freeTargets, flags: CommandFlags.DemandMaster); } - using (ETracer.StartInternalTrace("evt-src.sys.consumer.release.delay", + using (ETracer.StartInternalTrace("consumer.release.delay", t => PrepareTrace(t).Add("delay", releaseDelay))) { // let other potential consumer the chance of getting ownership @@ -1099,7 +1099,7 @@ private async ValueTask GetBucketAsync( { foreach (var strategy in strategies) { - using (ETracer.StartInternalTrace($"evt-src.sys.consumer.{strategy.Name}-storage.{storageType}.get")) + using (ETracer.StartInternalTrace($"consumer.{strategy.Name}-storage.{storageType}.get")) { bucket = await strategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } @@ -1107,7 +1107,7 @@ private async ValueTask GetBucketAsync( } else { - using (ETracer.StartInternalTrace($"evt-src.sys.consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) + using (ETracer.StartInternalTrace($"consumer.{_defaultStorageStrategy.Name}-storage.{storageType}.get")) { bucket = await _defaultStorageStrategy.LoadBucketAsync(meta, bucket, storageType, LocalGetProperty); } @@ -1133,7 +1133,7 @@ private async Task DelayIfEmpty(TimeSpan previousDelay, CancellationTo var newDelay = cfg.CalcNextDelay(previousDelay, cfg); var limitDelay = Min(cfg.MaxDelay.TotalMilliseconds, newDelay.TotalMilliseconds); newDelay = TimeSpan.FromMilliseconds(limitDelay); - using (ETracer.StartInternalTrace("evt-src.sys.consumer.delay.when-empty-queue", + using (ETracer.StartInternalTrace("consumer.delay.when-empty-queue", t => t.Add("delay", newDelay))) { await Task.Delay(newDelay, cancellationToken); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs index 996c0d0f..2f417c41 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProducerProvider/RedisProducerChannel.cs @@ -73,7 +73,7 @@ public async ValueTask SendAsync( string id = meta.MessageId; string env = meta.Environment.ToDash(); string uri = meta.UriDash; - using var activity = ETracer.StartInternalTrace($"evt-src.producer.{meta.Operation}.process", + using var activity = ETracer.StartInternalTrace($"producer.{meta.Operation}.process", t => t.Add("env", env) .Add("uri", uri) .Add("message-id", id)); diff --git a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs index 4ea67c3a..ff54c3e7 100644 --- a/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs +++ b/Channels/REDIS/EventSourcing.Backbone.Channels.RedisProvider.Common/RedisCommonProviderExtensions.cs @@ -47,7 +47,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( string fullUri = plan.FullUri(); StreamGroupInfo[] groupsInfo = Array.Empty(); - using var track = ETracer.StartInternalTrace("evt-src.sys.consumer.create-consumer-group", + using var track = ETracer.StartInternalTrace("consumer.create-consumer-group", t => PrepareTrace(t)); PrepareMeter(CreateConsumerGroupCounter).Add(1); @@ -71,7 +71,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( if (tryNumber > SPIN_LIMIT) { delay = Math.Min(delay * 2, MAX_DELAY); - using (ETracer.StartInternalTrace("evt-src.sys.consumer.delay.key-not-exists", + using (ETracer.StartInternalTrace("consumer.delay.key-not-exists", t => PrepareTrace(t) .Add("delay", delay) .Add("try-number", tryNumber))) @@ -100,7 +100,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( #endregion // Validation (if key exists) - using (ETracer.StartInternalTrace("evt-src.sys.consumer.get-consumer-group-info", + using (ETracer.StartInternalTrace("consumer.get-consumer-group-info", t => PrepareTrace(t) .Add("try-number", tryNumber))) { @@ -143,7 +143,7 @@ public static async Task CreateConsumerGroupIfNotExistsAsync( { try { - using (ETracer.StartInternalTrace("evt-src.sys.consumer.create-consumer-group", + using (ETracer.StartInternalTrace("consumer.create-consumer-group", t => PrepareTrace(t) .Add("try-number", tryNumber))) { diff --git a/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs b/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs index 0994fcf4..da6e15a8 100644 --- a/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs +++ b/Producers/EventSourcing.Backbone.Producers/ProducerTelemetryExtensions.cs @@ -28,8 +28,9 @@ public static class ProducerTelemetryExtensions tagsAction?.Invoke(t); - var activityName = activitySource.HasListeners() ? $"evt-src.producer.{meta.Environment.ToDash()}.{meta.UriDash}.{meta.Operation.ToDash()}.send" : string.Empty; + var activityName = activitySource.HasListeners() ? $"producer.{meta.Operation.ToDash()}.send" : string.Empty; Activity? activity = activitySource.StartActivity(activityName, ActivityKind.Producer); + meta.InjectTelemetryTags(activity); return activity; diff --git a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj index f7983efa..fbab0d74 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj +++ b/Tests/EventSourcing.Backbone.WebEventTest/EventSourcing.Backbone.WebEventTest.csproj @@ -17,6 +17,7 @@ + diff --git a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs index 6094bd5d..f22031f9 100644 --- a/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs +++ b/Tests/EventSourcing.Backbone.WebEventTest/OpenTelemetryExtensions.cs @@ -53,6 +53,7 @@ public static IServiceCollection AddOpenTelemetryEventSourcing(this WebApplicati // m.Enrich m.RecordException = true; }) + .AddGrpcClientInstrumentation() .AddOtlpExporter(); //if (environment.IsDevelopment()) // cfg.AddConsoleExporter(); From b047c0b3d471281b3f314814fa0488888175fdb1 Mon Sep 17 00:00:00 2001 From: Bnaya Eshet <3382669+bnayae@users.noreply.github.com> Date: Sun, 25 Jun 2023 15:13:51 +0300 Subject: [PATCH 178/178] buid: nuget upd --- .../Tests.Events.ConsumerWebTest.Service.csproj | 2 +- .../Tests.Events.ProducerWebTest.Service.csproj | 2 +- Tests/WebSampleS3/WebSampleS3.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj index 01586060..5bc4abe2 100644 --- a/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj +++ b/Tests/Specialized/Tests.Events.ConsumerWebTest.Service/Tests.Events.ConsumerWebTest.Service.csproj @@ -10,7 +10,7 @@ - + diff --git a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj index 3e6fe116..c5655d0c 100644 --- a/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj +++ b/Tests/Specialized/Tests.Events.ProducerWebTest.Service/Tests.Events.ProducerWebTest.Service.csproj @@ -10,7 +10,7 @@ - + diff --git a/Tests/WebSampleS3/WebSampleS3.csproj b/Tests/WebSampleS3/WebSampleS3.csproj index 8dcbeb70..51551c33 100644 --- a/Tests/WebSampleS3/WebSampleS3.csproj +++ b/Tests/WebSampleS3/WebSampleS3.csproj @@ -7,7 +7,7 @@ - +