From b01cfa7d98bc2f6bf646aa0e8b4692717544ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 10:17:25 +0300 Subject: [PATCH 01/21] Introduce IAbpDaprClientFactory --- .../Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs | 7 +++---- .../Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs | 8 ++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index faaf450ddc0..597c3e74ced 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -7,10 +7,11 @@ namespace Volo.Abp.Dapr; -public class AbpDaprClientFactory : ITransientDependency +public class AbpDaprClientFactory : ITransientDependency, IAbpDaprClientFactory { protected AbpDaprOptions Options { get; } protected AbpSystemTextJsonSerializerOptions SystemTextJsonSerializerOptions { get; } + private readonly static ConcurrentDictionary JsonSerializerOptionsCache = new(); public AbpDaprClientFactory( IOptions options, @@ -38,11 +39,9 @@ public virtual async Task CreateAsync() return builder.Build(); } - private readonly static ConcurrentDictionary JsonSerializerOptionsCache = new ConcurrentDictionary(); - protected virtual Task CreateJsonSerializerOptions() { return Task.FromResult(JsonSerializerOptionsCache.GetOrAdd(nameof(AbpDaprClientFactory), _ => new JsonSerializerOptions(SystemTextJsonSerializerOptions.JsonSerializerOptions))); } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs new file mode 100644 index 00000000000..12dc15bb670 --- /dev/null +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs @@ -0,0 +1,8 @@ +using Dapr.Client; + +namespace Volo.Abp.Dapr; + +public interface IAbpDaprClientFactory +{ + Task CreateAsync(); +} \ No newline at end of file From 0320fc81320d6b6d4e202f730a36b962ae7c78b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 10:20:59 +0300 Subject: [PATCH 02/21] No need to use ConcurrentDictionary since we only have a single item. --- .../Volo/Abp/Dapr/AbpDaprClientFactory.cs | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index 597c3e74ced..ed04aa9b03a 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Text.Json; +using System.Text.Json; using Dapr.Client; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -7,41 +6,39 @@ namespace Volo.Abp.Dapr; -public class AbpDaprClientFactory : ITransientDependency, IAbpDaprClientFactory +public class AbpDaprClientFactory : IAbpDaprClientFactory, ISingletonDependency { - protected AbpDaprOptions Options { get; } - protected AbpSystemTextJsonSerializerOptions SystemTextJsonSerializerOptions { get; } - private readonly static ConcurrentDictionary JsonSerializerOptionsCache = new(); + protected AbpDaprOptions DaprOptions { get; } + protected JsonSerializerOptions JsonSerializerOptions { get; } public AbpDaprClientFactory( IOptions options, IOptions systemTextJsonSerializerOptions) { - Options = options.Value; - SystemTextJsonSerializerOptions = systemTextJsonSerializerOptions.Value; + DaprOptions = options.Value; + JsonSerializerOptions = CreateJsonSerializerOptions(systemTextJsonSerializerOptions.Value); } - public virtual async Task CreateAsync() + protected virtual JsonSerializerOptions CreateJsonSerializerOptions(AbpSystemTextJsonSerializerOptions systemTextJsonSerializerOptions) + { + return new JsonSerializerOptions(systemTextJsonSerializerOptions.JsonSerializerOptions); + } + + public virtual Task CreateAsync() { var builder = new DaprClientBuilder() - .UseJsonSerializationOptions(await CreateJsonSerializerOptions()); + .UseJsonSerializationOptions(JsonSerializerOptions); - if (!Options.HttpEndpoint.IsNullOrWhiteSpace()) + if (!DaprOptions.HttpEndpoint.IsNullOrWhiteSpace()) { - builder.UseHttpEndpoint(Options.HttpEndpoint); + builder.UseHttpEndpoint(DaprOptions.HttpEndpoint); } - if (!Options.GrpcEndpoint.IsNullOrWhiteSpace()) + if (!DaprOptions.GrpcEndpoint.IsNullOrWhiteSpace()) { - builder.UseGrpcEndpoint(Options.GrpcEndpoint); + builder.UseGrpcEndpoint(DaprOptions.GrpcEndpoint); } - return builder.Build(); - } - - protected virtual Task CreateJsonSerializerOptions() - { - return Task.FromResult(JsonSerializerOptionsCache.GetOrAdd(nameof(AbpDaprClientFactory), - _ => new JsonSerializerOptions(SystemTextJsonSerializerOptions.JsonSerializerOptions))); + return Task.FromResult(builder.Build()); } } \ No newline at end of file From 0064f4f501a15ac86f7433aeb4dbdd9495fa938e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 11:00:17 +0300 Subject: [PATCH 03/21] Introduce IAbpDaprClientFactory.CreateHttpClientAsync Also improved the CreateAsync method. --- .../Volo/Abp/Dapr/AbpDaprClientFactory.cs | 15 ++++++++++++++- .../Volo/Abp/Dapr/IAbpDaprClientFactory.cs | 8 +++++++- .../Dapr/DaprAbpDistributedLock.cs | 4 ++-- .../Abp/EventBus/Dapr/DaprDistributedEventBus.cs | 4 ++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index ed04aa9b03a..b7419153ff5 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -24,7 +24,7 @@ protected virtual JsonSerializerOptions CreateJsonSerializerOptions(AbpSystemTex return new JsonSerializerOptions(systemTextJsonSerializerOptions.JsonSerializerOptions); } - public virtual Task CreateAsync() + public virtual Task CreateAsync(Action? builderAction = null) { var builder = new DaprClientBuilder() .UseJsonSerializationOptions(JsonSerializerOptions); @@ -39,6 +39,19 @@ public virtual Task CreateAsync() builder.UseGrpcEndpoint(DaprOptions.GrpcEndpoint); } + builderAction?.Invoke(builder); + return Task.FromResult(builder.Build()); } + + public Task CreateHttpClientAsync(string? appId = null, string? daprEndpoint = null, string? daprApiToken = null) + { + if(daprEndpoint.IsNullOrWhiteSpace() && + !DaprOptions.HttpEndpoint.IsNullOrWhiteSpace()) + { + daprEndpoint = DaprOptions.HttpEndpoint; + } + + return Task.FromResult(DaprClient.CreateInvokeHttpClient(appId, daprEndpoint, daprApiToken)); + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs index 12dc15bb670..cd2916ae5a1 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs @@ -4,5 +4,11 @@ namespace Volo.Abp.Dapr; public interface IAbpDaprClientFactory { - Task CreateAsync(); + Task CreateAsync(Action? builderAction = null); + + Task CreateHttpClientAsync( + string? appId = null, + string? daprEndpoint = null, + string? daprApiToken = null + ); } \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs index b4116c105c8..7221775fd11 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs @@ -7,13 +7,13 @@ namespace Volo.Abp.DistributedLocking.Dapr; [Dependency(ReplaceServices = true)] public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency { - protected AbpDaprClientFactory DaprClientFactory { get; } + protected IAbpDaprClientFactory DaprClientFactory { get; } protected AbpDistributedLockDaprOptions DistributedLockDaprOptions { get; } protected AbpDaprOptions DaprOptions { get; } protected IDistributedLockKeyNormalizer DistributedLockKeyNormalizer { get; } public DaprAbpDistributedLock( - AbpDaprClientFactory daprClientFactory, + IAbpDaprClientFactory daprClientFactory, IOptions distributedLockDaprOptions, IOptions daprOptions, IDistributedLockKeyNormalizer distributedLockKeyNormalizer) diff --git a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs index 1f241b9c6d5..19ff8208772 100644 --- a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs @@ -18,7 +18,7 @@ public class DaprDistributedEventBus : DistributedEventBusBase, ISingletonDepend { protected IDaprSerializer Serializer { get; } protected AbpDaprEventBusOptions DaprEventBusOptions { get; } - protected AbpDaprClientFactory DaprClientFactory { get; } + protected IAbpDaprClientFactory DaprClientFactory { get; } protected ConcurrentDictionary> HandlerFactories { get; } protected ConcurrentDictionary EventTypes { get; } @@ -33,7 +33,7 @@ public DaprDistributedEventBus( IEventHandlerInvoker eventHandlerInvoker, IDaprSerializer serializer, IOptions daprEventBusOptions, - AbpDaprClientFactory daprClientFactory) + IAbpDaprClientFactory daprClientFactory) : base(serviceScopeFactory, currentTenant, unitOfWorkManager, abpDistributedEventBusOptions, guidGenerator, clock, eventHandlerInvoker) { Serializer = serializer; From ea2cf047621c009d133dc732e124583fa2578abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 11:58:57 +0300 Subject: [PATCH 04/21] Intro to Dapr document --- docs/en/Dapr/Index.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/en/Dapr/Index.md diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md new file mode 100644 index 00000000000..79d12346b5d --- /dev/null +++ b/docs/en/Dapr/Index.md @@ -0,0 +1,10 @@ +# ABP Dapr Integration + +> This document assumes that you are already familiar with [Dapr](https://dapr.io/) and you want to use it in your ABP based applications. + +[Dapr](https://dapr.io/) (Distributed Application Runtime) provides APIs that simplify microservice connectivity. It is an open source project that is mainly backed by Microsoft. It is also a CNCF (Cloud Native Computing Foundation) project and trusted by community. + +ABP and Dapr has some intersecting features like service-to-service communication, distributed message bus and distributed locking. However their purposes are totally different. ABP's goal is to provide an end-to-end developer experience by offering an opinionated architecture and providing the necessary infrastructure libraries, reusable modules and tools to implement that architecture properly. Dapr's purpose, on the other hand, is to provide a runtime to decouple common microservice communication patterns from your application logic. + +ABP and Dapr can perfectly work together in the same application. You can just follow [its documentation](https://docs.dapr.io/) and use it as is. However, ABP offers some packages to provide better integration where Dapr features intersect with ABP. You can use other Dapr features with no ABP integration packages based on [its documentation](https://docs.dapr.io/). + From 03936d0d25589ca7312c4b69086a16c87acd7019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 16:57:19 +0300 Subject: [PATCH 05/21] Remove AbpDaprOptions.AppId, introduce AbpDistributedLockDaprOptions.Owner. --- .../Volo/Abp/Dapr/AbpDaprClientFactory.cs | 5 ++++- .../Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs | 2 -- .../Dapr/AbpDistributedLockDaprOptions.cs | 2 ++ .../Dapr/DaprAbpDistributedLock.cs | 13 +++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index b7419153ff5..b678b442db1 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -44,7 +44,10 @@ public virtual Task CreateAsync(Action? builderAc return Task.FromResult(builder.Build()); } - public Task CreateHttpClientAsync(string? appId = null, string? daprEndpoint = null, string? daprApiToken = null) + public Task CreateHttpClientAsync( + string? appId = null, + string? daprEndpoint = null, + string? daprApiToken = null) { if(daprEndpoint.IsNullOrWhiteSpace() && !DaprOptions.HttpEndpoint.IsNullOrWhiteSpace()) diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs index 9d743263c5c..e6919783afd 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs @@ -2,8 +2,6 @@ public class AbpDaprOptions { - public string AppId { get; set; } - public string HttpEndpoint { get; set; } public string GrpcEndpoint { get; set; } diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs index 43feba3c3be..c2688abd1b7 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs @@ -4,6 +4,8 @@ public class AbpDistributedLockDaprOptions { public string StoreName { get; set; } + public string? Owner { get; set; } + public TimeSpan DefaultTimeout { get; set; } public AbpDistributedLockDaprOptions() diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs index 7221775fd11..3b4a878456e 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs @@ -9,22 +9,19 @@ public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency { protected IAbpDaprClientFactory DaprClientFactory { get; } protected AbpDistributedLockDaprOptions DistributedLockDaprOptions { get; } - protected AbpDaprOptions DaprOptions { get; } protected IDistributedLockKeyNormalizer DistributedLockKeyNormalizer { get; } public DaprAbpDistributedLock( IAbpDaprClientFactory daprClientFactory, IOptions distributedLockDaprOptions, - IOptions daprOptions, IDistributedLockKeyNormalizer distributedLockKeyNormalizer) { DaprClientFactory = daprClientFactory; DistributedLockKeyNormalizer = distributedLockKeyNormalizer; - DaprOptions = daprOptions.Value; DistributedLockDaprOptions = distributedLockDaprOptions.Value; } - public async Task TryAcquireAsync( + public async Task TryAcquireAsync( string name, TimeSpan timeout = default, CancellationToken cancellationToken = default) @@ -34,13 +31,13 @@ public async Task TryAcquireAsync( timeout = DistributedLockDaprOptions.DefaultTimeout; } - var daprClient = await DaprClientFactory.CreateAsync(); - var key = DistributedLockKeyNormalizer.NormalizeKey(name); + name = DistributedLockKeyNormalizer.NormalizeKey(name); + var daprClient = await DaprClientFactory.CreateAsync(); var lockResponse = await daprClient.Lock( DistributedLockDaprOptions.StoreName, - key, - DaprOptions.AppId, + name, + DistributedLockDaprOptions.Owner ?? Guid.NewGuid().ToString(), (int)timeout.TotalSeconds, cancellationToken); From e8d1051157f307e43d40d7f91d8835e539f1d0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 16:57:26 +0300 Subject: [PATCH 06/21] Update Index.md --- docs/en/Dapr/Index.md | 77 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index 79d12346b5d..861bb605956 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -6,5 +6,80 @@ ABP and Dapr has some intersecting features like service-to-service communication, distributed message bus and distributed locking. However their purposes are totally different. ABP's goal is to provide an end-to-end developer experience by offering an opinionated architecture and providing the necessary infrastructure libraries, reusable modules and tools to implement that architecture properly. Dapr's purpose, on the other hand, is to provide a runtime to decouple common microservice communication patterns from your application logic. -ABP and Dapr can perfectly work together in the same application. You can just follow [its documentation](https://docs.dapr.io/) and use it as is. However, ABP offers some packages to provide better integration where Dapr features intersect with ABP. You can use other Dapr features with no ABP integration packages based on [its documentation](https://docs.dapr.io/). +ABP and Dapr can perfectly work together in the same application. ABP offers some packages to provide better integration where Dapr features intersect with ABP. You can use other Dapr features with no ABP integration packages based on [its own documentation](https://docs.dapr.io/). +## ABP Dapr Integration Packages + +ABP provides the following NuGet packages for the Dapr integration: + +* [Volo.Abp.Dapr](https://www.nuget.org/packages/Volo.Abp.Dapr): The main Dapr integration package. All other packages depend on this package. +* [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr): Implements ABP's distributed event bus with Dapr's [publish & subscribe](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) building block. With this package, you can send events, but can not receive. +* [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus): Provides the endpoints to receive events from Dapr's [publish & subscribe](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) building block. Use this package to send and receive events. +* [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr): Integration package for ABP's [dynamic](../API/Dynamic-CSharp-API-Clients.md) and [static](../API/Static-CSharp-API-Clients.md) C# API Client Proxies systems with Dapr's [service invocation](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/) building block. +* [Volo.Abp.DistributedLocking.Dapr](https://www.nuget.org/packages/Volo.Abp.DistributedLocking.Dapr): Uses Dapr's [distributed lock](https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/) building block for [distributed locking](Distributed-Locking.md) service of the ABP Framework. + +In the following sections, we will see how to use these packages to use Dapr in your ABP based solutions. + +## AbpDaprOptions + +`AbpDaprOptions` is the main [options class](../Options.md) that you can configure the global Dapr settings. **All settings are optional and you mostly don't need to configure them.** If you need, you can configure it in the `ConfigureServices` method of your [module class](../Module-Development-Basics.md): + +````csharp +Configure(options => +{ + // ... +}); +```` + +Available properties of the `AbpDaprOptions` class: + +* `HttpEndpoint` (optional): HTTP endpoint that is used while creating a `DaprClient` object. If you don't specify, the default value is used. +* `GrpcEndpoint` (optional): The gRPC endpoint that is used while creating a `DaprClient` object. If you don't specify, the default value is used. + +Alternatively, you can configure the options in the `Dapr` section of your `appsettings.json` file. Example: + +````csharp +"Dapr": { + "HttpEndpoint": "http://localhost:3500/" +} +```` + +## IAbpDaprClientFactory + +`IAbpDaprClientFactory` is used to create `DaprClient` or `HttpClient` objects to perform operations on Dapr. It uses `AbpDaprOptions`, so you can configure the settings in a central place. + +**Example usages:** + +````csharp +public class MyService +{ + private readonly IAbpDaprClientFactory _daprClientFactory; + + public MyService(IAbpDaprClientFactory daprClientFactory) + { + _daprClientFactory = daprClientFactory; + } + + public async Task DoItAsync() + { + // Create a DaprClient object with default options + DaprClient daprClient = await _daprClientFactory.CreateAsync(); + + /* Create a DaprClient object with configuring + * the DaprClientBuilder object */ + DaprClient daprClient2 = await _daprClientFactory + .CreateAsync(builder => + { + builder.UseDaprApiToken("..."); + }); + + // Create an HttpClient object + HttpClient httpClient = await _daprClientFactory + .CreateHttpClientAsync("target-app-id"); + } +} +```` + +`CreateHttpClientAsync` method also gets optional `daprEndpoint` and `daprApiToken` parameters. + +> ABP uses `IAbpDaprClientFactory` when it needs to create a Dapr client. You can also use Dapr API to create client objects in your application. Using `IAbpDaprClientFactory` is recommended, but not required. \ No newline at end of file From 63c3f30157708a66986d34dcd19215c3dd18c5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 17:22:05 +0300 Subject: [PATCH 07/21] Documented C# API Client Proxies Integration --- docs/en/Dapr/Index.md | 61 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index 861bb605956..888e8fa5233 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -13,14 +13,28 @@ ABP and Dapr can perfectly work together in the same application. ABP offers som ABP provides the following NuGet packages for the Dapr integration: * [Volo.Abp.Dapr](https://www.nuget.org/packages/Volo.Abp.Dapr): The main Dapr integration package. All other packages depend on this package. +* [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr): Integration package for ABP's [dynamic](../API/Dynamic-CSharp-API-Clients.md) and [static](../API/Static-CSharp-API-Clients.md) C# API Client Proxies systems with Dapr's [service invocation](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/) building block. * [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr): Implements ABP's distributed event bus with Dapr's [publish & subscribe](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) building block. With this package, you can send events, but can not receive. * [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus): Provides the endpoints to receive events from Dapr's [publish & subscribe](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) building block. Use this package to send and receive events. -* [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr): Integration package for ABP's [dynamic](../API/Dynamic-CSharp-API-Clients.md) and [static](../API/Static-CSharp-API-Clients.md) C# API Client Proxies systems with Dapr's [service invocation](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/) building block. * [Volo.Abp.DistributedLocking.Dapr](https://www.nuget.org/packages/Volo.Abp.DistributedLocking.Dapr): Uses Dapr's [distributed lock](https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/) building block for [distributed locking](Distributed-Locking.md) service of the ABP Framework. In the following sections, we will see how to use these packages to use Dapr in your ABP based solutions. -## AbpDaprOptions +## Basics + +### Installation + +> This section explains how to add [Volo.Abp.Dapr](https://www.nuget.org/packages/Volo.Abp.Dapr), the core Dapr integration package to your project. If you are using one of the other Dapr integration packages, you can skip this section since this package will be indirectly added. + +Use the ABP CLI to add [Volo.Abp.Dapr](https://www.nuget.org/packages/Volo.Abp.Dapr) NuGet package to your project: + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.Dapr` package. +* Run `abp add-package Volo.Abp.Dapr` command. + +If you want to do it manually, install the [Volo.Abp.Dapr](https://www.nuget.org/packages/Volo.Abp.Dapr) NuGet package to your project and add `[DependsOn(typeof(AbpDaprModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. + +### AbpDaprOptions `AbpDaprOptions` is the main [options class](../Options.md) that you can configure the global Dapr settings. **All settings are optional and you mostly don't need to configure them.** If you need, you can configure it in the `ConfigureServices` method of your [module class](../Module-Development-Basics.md): @@ -44,7 +58,7 @@ Alternatively, you can configure the options in the `Dapr` section of your `apps } ```` -## IAbpDaprClientFactory +### IAbpDaprClientFactory `IAbpDaprClientFactory` is used to create `DaprClient` or `HttpClient` objects to perform operations on Dapr. It uses `AbpDaprOptions`, so you can configure the settings in a central place. @@ -82,4 +96,43 @@ public class MyService `CreateHttpClientAsync` method also gets optional `daprEndpoint` and `daprApiToken` parameters. -> ABP uses `IAbpDaprClientFactory` when it needs to create a Dapr client. You can also use Dapr API to create client objects in your application. Using `IAbpDaprClientFactory` is recommended, but not required. \ No newline at end of file +> ABP uses `IAbpDaprClientFactory` when it needs to create a Dapr client. You can also use Dapr API to create client objects in your application. Using `IAbpDaprClientFactory` is recommended, but not required. + +## C# API Client Proxies Integration + +ABP can [dynamically](../API/Dynamic-CSharp-API-Clients.md) or [statically](../API/Static-CSharp-API-Clients.md) generate proxy classes to invoke your HTTP APIs from a Dotnet client application. It makes dead simple to consume HTTP APIs in a distributed system. [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) package configures the client-side proxies system, so it uses Dapr for the communication between your applications. + +### Installation + +Use the ABP CLI to add [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) NuGet package to your project (to the client side): + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.Http.Client.Dapr` package. +* Run `abp add-package Volo.Abp.Http.Client.Dapr` command. + +If you want to do it manually, install the [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) NuGet package to your project and add `[DependsOn(typeof(AbpHttpClientDaprModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. + +### Configuration + +One you install the [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) NuGet package, all you need to do it to configure ABP's remote services option either in `appsettings.json` or using the `AbpRemoteServiceOptions` [options class](). + +**Example:** + +````csharp +{ + "RemoteServices": { + "Default": { + "BaseUrl": "http://dapr-httpapi/" + } + } +} +```` + +`dapr-httpapi` in this example is the application id of the server application in your Dapr configuration. + +The remove service name (`Default` in this example) should match the remote service name specified in the `AddHttpClientProxies` call for dynamic client proxies or `AddStaticHttpClientProxies` call for static client proxies. Using `Default` is fine if your client communicates to a single server. However, if your client uses multiple servers, you typically have multiple keys in the `RemoteServices` configuration. Once you configure the remote service endpoints as Dapr application ids, it will automatically work and make the HTTP calls through Dapr when you use ABP's client proxy system. + +> See the [dynamic](../API/Dynamic-CSharp-API-Clients.md) and [static](../API/Static-CSharp-API-Clients.md) client proxy documents for details about the ABP's client proxy system. + + + From e25ab604b396f4ee536df9777c05bb5a855a7458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 18:26:55 +0300 Subject: [PATCH 08/21] Distributed Event Bus Integration section --- docs/en/Dapr/Index.md | 59 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index 888e8fa5233..669fa070a62 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -100,7 +100,7 @@ public class MyService ## C# API Client Proxies Integration -ABP can [dynamically](../API/Dynamic-CSharp-API-Clients.md) or [statically](../API/Static-CSharp-API-Clients.md) generate proxy classes to invoke your HTTP APIs from a Dotnet client application. It makes dead simple to consume HTTP APIs in a distributed system. [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) package configures the client-side proxies system, so it uses Dapr for the communication between your applications. +ABP can [dynamically](../API/Dynamic-CSharp-API-Clients.md) or [statically](../API/Static-CSharp-API-Clients.md) generate proxy classes to invoke your HTTP APIs from a Dotnet client application. It makes dead simple to consume HTTP APIs in a distributed system. [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) package configures the client-side proxies system, so it uses Dapr's service invocation building block for the communication between your applications. ### Installation @@ -114,7 +114,7 @@ If you want to do it manually, install the [Volo.Abp.Http.Client.Dapr](https://w ### Configuration -One you install the [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) NuGet package, all you need to do it to configure ABP's remote services option either in `appsettings.json` or using the `AbpRemoteServiceOptions` [options class](). +One you install the [Volo.Abp.Http.Client.Dapr](https://www.nuget.org/packages/Volo.Abp.Http.Client.Dapr) NuGet package, all you need to do it to configure ABP's remote services option either in `appsettings.json` or using the `AbpRemoteServiceOptions` [options class](../Options.md). **Example:** @@ -134,5 +134,60 @@ The remove service name (`Default` in this example) should match the remote serv > See the [dynamic](../API/Dynamic-CSharp-API-Clients.md) and [static](../API/Static-CSharp-API-Clients.md) client proxy documents for details about the ABP's client proxy system. +## Distributed Event Bus Integration +[ABP's distributed event bus](../Distributed-Event-Bus.md) system provides a convenient abstraction to allow application communicate asynchronously via events. ABP has integration packages with various distributed messaging systems, like RabbitMQ, Kafka, and Azure. Dapr also has a [publish & subscribe building block](https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-overview/) for the same purpose: distributed messaging / events. +ABP's [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr) and [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) packages make possible to use the Dapr infrastructure for ABP's distributed event bus. + +The [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr) package can be used by any type of application (e.g., a Console or ASP.NET Core application) to publish events through Dapr. To be able to receive messages (by subscribing events), you need to have the [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) package installed, and your application should be an ASP.NET Core application. + +### Installation + +If your application is an ASP.NET Core application and you want to send and receive events, you need to install the [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) package as describe below: + +You can use the ABP CLI to add [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) NuGet package to your project (to the client side): + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.AspNetCore.Mvc.Dapr.EventBus` package. +* Run `abp add-package Volo.Abp.AspNetCore.Mvc.Dapr.EventBus` command. + +If you want to do it manually, install the [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) NuGet package to your project and add `[DependsOn(typeof(AbpAspNetCoreMvcDaprEventBusModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. + +> **If you install the [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) package, you don't need to install the [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr) package, because the first one already has a reference to the latter one.** + +If your application is not an ASP.NET Core application, you can't receive events from Dapr, at least with ABP's integration packages (see [Dapr's document](https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/) if you want to receive events in a different type of application). However, you can still publish messages using the [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr) package. In this case, follow the steps below to install that package to your project: + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.EventBus.Dapr` package. +* Run `abp add-package Volo.Abp.EventBus.Dapr` command. + +If you want to do it manually, install the [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr) NuGet package to your project and add `[DependsOn(typeof(AbpEventBusDaprModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. + +### Configuration + +You can configure the `AbpDaprEventBusOptions` [options class](../Options.md) for Dapr configuration: + +````csharp +Configure(options => +{ + options.PubSubName = "test-pubsub"; +}); +```` + +Available properties of the `AbpDaprEventBusOptions` class: + +* `PubSubName` (optional): The `pubsubName` parameter while publishing messages through `DaprClient.PublishEventAsync` method. Default value: `pubsub`. + +### Usage + +You can follow [ABP's distributed event bus documentation](../Distributed-Event-Bus.md) to learn how to publish and subscribe to events in the ABP way. No change required in your application code to use Dapr pub-sub. + +In addition to ABP's standard distributed event bus system, you can also use Dapr's API to publish events. In that case, just use the `IAbpDaprClientFactory` service to create a `DaprClient` object (or create it yourself by following Dapr's documentation) and use its `PublishEventAsync` method as [documented by Dapr](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/publish-subscribe). + +> If you directly use the Dapr API to publish events, you can not benefit from ABP's standard distributed event bus features, like the outbox/inbox pattern implementation. + +## See Also + +* [Dapr for .NET Developers](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/) +* [The Official Dapr Documentation](https://docs.dapr.io/) \ No newline at end of file From c58a59373e7b342718f8313b6cc88958fb8e8d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 14 Sep 2022 21:36:27 +0300 Subject: [PATCH 09/21] Set nullable: AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter._writeJsonSerializerOptions --- .../AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs index fa26d35ca69..bcd2a0e1be6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/SystemTextJson/AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter.cs @@ -6,7 +6,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson; public class AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter : JsonConverter { - private JsonSerializerOptions _writeJsonSerializerOptions; + private JsonSerializerOptions? _writeJsonSerializerOptions; public override AbpAspNetCoreMvcDaprSubscriptionDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { From 5b93208433d39095406533d9abc3f4a060396066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 08:34:32 +0300 Subject: [PATCH 10/21] Introduce IDaprApiTokenProvider & AbpDaprOptions.DaprApiToken --- .../Volo/Abp/Dapr/AbpDaprClientFactory.cs | 35 +++++++++++++------ .../Volo/Abp/Dapr/AbpDaprOptions.cs | 4 ++- .../Volo/Abp/Dapr/DaprApiTokenProvider.cs | 19 ++++++++++ .../Volo/Abp/Dapr/IDaprApiTokenProvider.cs | 6 ++++ 4 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs create mode 100644 framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index b678b442db1..4f6fde67b46 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -10,21 +10,19 @@ public class AbpDaprClientFactory : IAbpDaprClientFactory, ISingletonDependency { protected AbpDaprOptions DaprOptions { get; } protected JsonSerializerOptions JsonSerializerOptions { get; } + protected IDaprApiTokenProvider DaprApiTokenProvider { get; } public AbpDaprClientFactory( IOptions options, - IOptions systemTextJsonSerializerOptions) + IOptions systemTextJsonSerializerOptions, + IDaprApiTokenProvider daprApiTokenProvider) { + DaprApiTokenProvider = daprApiTokenProvider; DaprOptions = options.Value; JsonSerializerOptions = CreateJsonSerializerOptions(systemTextJsonSerializerOptions.Value); } - protected virtual JsonSerializerOptions CreateJsonSerializerOptions(AbpSystemTextJsonSerializerOptions systemTextJsonSerializerOptions) - { - return new JsonSerializerOptions(systemTextJsonSerializerOptions.JsonSerializerOptions); - } - - public virtual Task CreateAsync(Action? builderAction = null) + public virtual async Task CreateAsync(Action? builderAction = null) { var builder = new DaprClientBuilder() .UseJsonSerializationOptions(JsonSerializerOptions); @@ -39,12 +37,18 @@ public virtual Task CreateAsync(Action? builderAc builder.UseGrpcEndpoint(DaprOptions.GrpcEndpoint); } + var apiToken = await DaprApiTokenProvider.GetAsync(); + if (!apiToken.IsNullOrWhiteSpace()) + { + builder.UseDaprApiToken(apiToken); + } + builderAction?.Invoke(builder); - return Task.FromResult(builder.Build()); + return builder.Build(); } - public Task CreateHttpClientAsync( + public virtual async Task CreateHttpClientAsync( string? appId = null, string? daprEndpoint = null, string? daprApiToken = null) @@ -54,7 +58,16 @@ public Task CreateHttpClientAsync( { daprEndpoint = DaprOptions.HttpEndpoint; } - - return Task.FromResult(DaprClient.CreateInvokeHttpClient(appId, daprEndpoint, daprApiToken)); + + return DaprClient.CreateInvokeHttpClient( + appId, + daprEndpoint, + daprApiToken ?? await DaprApiTokenProvider.GetAsync() + ); + } + + protected virtual JsonSerializerOptions CreateJsonSerializerOptions(AbpSystemTextJsonSerializerOptions systemTextJsonSerializerOptions) + { + return new JsonSerializerOptions(systemTextJsonSerializerOptions.JsonSerializerOptions); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs index e6919783afd..1453c0aac04 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs @@ -5,4 +5,6 @@ public class AbpDaprOptions public string HttpEndpoint { get; set; } public string GrpcEndpoint { get; set; } -} + + public string DaprApiToken { get; set; } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs new file mode 100644 index 00000000000..8f0cd104b07 --- /dev/null +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Dapr; + +public class DaprApiTokenProvider : IDaprApiTokenProvider, ISingletonDependency +{ + public AbpDaprOptions Options { get; } + + public DaprApiTokenProvider(IOptions options) + { + Options = options.Value; + } + + public virtual Task GetAsync() + { + return Task.FromResult(Options.DaprApiToken); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs new file mode 100644 index 00000000000..e122d3c201f --- /dev/null +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Dapr; + +public interface IDaprApiTokenProvider +{ + Task GetAsync(); +} \ No newline at end of file From 7ec42e55cc33a817f13ee29db98d05f2854ecdae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 08:58:54 +0300 Subject: [PATCH 11/21] Register DaprClient to DI Also, made factory methods sync. --- .../Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs | 8 ++++---- .../src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs | 7 +++++++ .../Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs | 4 ++-- .../Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs | 4 ++-- .../Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs | 2 +- .../Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs | 2 +- .../Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs | 2 +- 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index 4f6fde67b46..255ff1b5171 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -22,7 +22,7 @@ public AbpDaprClientFactory( JsonSerializerOptions = CreateJsonSerializerOptions(systemTextJsonSerializerOptions.Value); } - public virtual async Task CreateAsync(Action? builderAction = null) + public virtual DaprClient Create(Action? builderAction = null) { var builder = new DaprClientBuilder() .UseJsonSerializationOptions(JsonSerializerOptions); @@ -37,7 +37,7 @@ public virtual async Task CreateAsync(Action? bui builder.UseGrpcEndpoint(DaprOptions.GrpcEndpoint); } - var apiToken = await DaprApiTokenProvider.GetAsync(); + var apiToken = DaprApiTokenProvider.Get(); if (!apiToken.IsNullOrWhiteSpace()) { builder.UseDaprApiToken(apiToken); @@ -48,7 +48,7 @@ public virtual async Task CreateAsync(Action? bui return builder.Build(); } - public virtual async Task CreateHttpClientAsync( + public virtual HttpClient CreateHttpClient( string? appId = null, string? daprEndpoint = null, string? daprApiToken = null) @@ -62,7 +62,7 @@ public virtual async Task CreateHttpClientAsync( return DaprClient.CreateInvokeHttpClient( appId, daprEndpoint, - daprApiToken ?? await DaprApiTokenProvider.GetAsync() + daprApiToken ?? DaprApiTokenProvider.Get() ); } diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs index 43d6d11033c..f4210ea1047 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Json; using Volo.Abp.Modularity; @@ -11,5 +12,11 @@ public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); Configure(configuration.GetSection("Dapr")); + + context.Services.TryAddSingleton( + serviceProvider => serviceProvider + .GetRequiredService() + .Create() + ); } } diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs index 8f0cd104b07..88f53da2203 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs @@ -12,8 +12,8 @@ public DaprApiTokenProvider(IOptions options) Options = options.Value; } - public virtual Task GetAsync() + public virtual string Get() { - return Task.FromResult(Options.DaprApiToken); + return Options.DaprApiToken; } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs index cd2916ae5a1..fc50a07d417 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IAbpDaprClientFactory.cs @@ -4,9 +4,9 @@ namespace Volo.Abp.Dapr; public interface IAbpDaprClientFactory { - Task CreateAsync(Action? builderAction = null); + DaprClient Create(Action? builderAction = null); - Task CreateHttpClientAsync( + HttpClient CreateHttpClient( string? appId = null, string? daprEndpoint = null, string? daprApiToken = null diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs index e122d3c201f..f08e5d48538 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs @@ -2,5 +2,5 @@ namespace Volo.Abp.Dapr; public interface IDaprApiTokenProvider { - Task GetAsync(); + string Get(); } \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs index 3b4a878456e..d055376fb8a 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs @@ -33,7 +33,7 @@ public DaprAbpDistributedLock( name = DistributedLockKeyNormalizer.NormalizeKey(name); - var daprClient = await DaprClientFactory.CreateAsync(); + var daprClient = DaprClientFactory.Create(); var lockResponse = await daprClient.Lock( DistributedLockDaprOptions.StoreName, name, diff --git a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs index 19ff8208772..c8d47f6887f 100644 --- a/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.Dapr/Volo/Abp/EventBus/Dapr/DaprDistributedEventBus.cs @@ -197,7 +197,7 @@ protected virtual async Task PublishToDaprAsync(Type eventType, object eventData protected virtual async Task PublishToDaprAsync(string eventName, object eventData) { - var client = await DaprClientFactory.CreateAsync(); + var client = DaprClientFactory.Create(); await client.PublishEventAsync(pubsubName: DaprEventBusOptions.PubSubName, topicName: eventName, data: eventData); } From 655e4d1538d1e25b3907e097fc9dbff8c5cb7aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 09:14:38 +0300 Subject: [PATCH 12/21] Introduce AbpDaprOptions.AppApiToken Also set defaults from environment variables. --- .../Volo/Abp/Dapr/AbpDaprClientFactory.cs | 4 +- .../Volo/Abp/Dapr/AbpDaprModule.cs | 47 ++++++++++++++++++- .../Volo/Abp/Dapr/AbpDaprOptions.cs | 2 + .../Volo/Abp/Dapr/DaprApiTokenProvider.cs | 9 +++- .../Volo/Abp/Dapr/IDaprApiTokenProvider.cs | 3 +- 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs index 255ff1b5171..ad284ce156c 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprClientFactory.cs @@ -37,7 +37,7 @@ public virtual DaprClient Create(Action? builderAction = null builder.UseGrpcEndpoint(DaprOptions.GrpcEndpoint); } - var apiToken = DaprApiTokenProvider.Get(); + var apiToken = DaprApiTokenProvider.GetDaprApiToken(); if (!apiToken.IsNullOrWhiteSpace()) { builder.UseDaprApiToken(apiToken); @@ -62,7 +62,7 @@ public virtual HttpClient CreateHttpClient( return DaprClient.CreateInvokeHttpClient( appId, daprEndpoint, - daprApiToken ?? DaprApiTokenProvider.Get() + daprApiToken ?? DaprApiTokenProvider.GetDaprApiToken() ); } diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs index f4210ea1047..276e96895d7 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Json; using Volo.Abp.Modularity; @@ -11,7 +12,8 @@ public class AbpDaprModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); - Configure(configuration.GetSection("Dapr")); + + ConfigureDaprOptions(configuration); context.Services.TryAddSingleton( serviceProvider => serviceProvider @@ -19,4 +21,45 @@ public override void ConfigureServices(ServiceConfigurationContext context) .Create() ); } + + private void ConfigureDaprOptions(IConfiguration configuration) + { + Configure(configuration.GetSection("Dapr")); + Configure(options => + { + if (options.DaprApiToken.IsNullOrWhiteSpace()) + { + var confEnv = configuration["DAPR_API_TOKEN"]; + if (!confEnv.IsNullOrWhiteSpace()) + { + options.DaprApiToken = confEnv; + } + else + { + var env = Environment.GetEnvironmentVariable("DAPR_API_TOKEN"); + if (!env.IsNullOrWhiteSpace()) + { + options.DaprApiToken = env; + } + } + } + + if (options.AppApiToken.IsNullOrWhiteSpace()) + { + var confEnv = configuration["APP_API_TOKEN"]; + if (!confEnv.IsNullOrWhiteSpace()) + { + options.AppApiToken = confEnv; + } + else + { + var env = Environment.GetEnvironmentVariable("APP_API_TOKEN"); + if (!env.IsNullOrWhiteSpace()) + { + options.AppApiToken = env; + } + } + } + }); + } } diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs index 1453c0aac04..7ac19ad31c0 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/AbpDaprOptions.cs @@ -7,4 +7,6 @@ public class AbpDaprOptions public string GrpcEndpoint { get; set; } public string DaprApiToken { get; set; } + + public string AppApiToken { get; set; } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs index 88f53da2203..326d4466756 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs @@ -5,15 +5,20 @@ namespace Volo.Abp.Dapr; public class DaprApiTokenProvider : IDaprApiTokenProvider, ISingletonDependency { - public AbpDaprOptions Options { get; } + protected AbpDaprOptions Options { get; } public DaprApiTokenProvider(IOptions options) { Options = options.Value; } - public virtual string Get() + public virtual string GetDaprApiToken() { return Options.DaprApiToken; } + + public virtual string GetAppApiToken() + { + return Options.AppApiToken; + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs index f08e5d48538..260b0ea7091 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs @@ -2,5 +2,6 @@ namespace Volo.Abp.Dapr; public interface IDaprApiTokenProvider { - string Get(); + string GetDaprApiToken(); + string GetAppApiToken(); } \ No newline at end of file From 0ec37b1c4af2ac48ba78ac95e92f6e7f5df8f2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 10:03:58 +0300 Subject: [PATCH 13/21] Validate Dapr App API Tokens. --- .../AbpAspNetCoreMvcDaprPubSubController.cs | 2 + .../Mvc/Dapr/DaprAppApiTokenValidator.cs | 62 +++++++++++++++++++ .../Mvc/Dapr/DaprHttpContextExtensions.cs | 31 ++++++++++ .../Mvc/Dapr/IDaprAppApiTokenValidator.cs | 10 +++ 4 files changed, 105 insertions(+) create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs index c97e793b537..674e227cd00 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus/Volo/Abp/AspNetCore/Mvc/Dapr/EventBus/Controllers/AbpAspNetCoreMvcDaprPubSubController.cs @@ -21,6 +21,8 @@ public virtual async Task> Subs [HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)] public virtual async Task EventsAsync() { + this.HttpContext.ValidateDaprAppApiToken(); + var bodyJsonDocument = await JsonDocument.ParseAsync(HttpContext.Request.Body); var request = JsonSerializer.Deserialize(bodyJsonDocument.RootElement.GetRawText(), HttpContext.RequestServices.GetRequiredService>().Value.JsonSerializerOptions); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs new file mode 100644 index 00000000000..aebfabeecd0 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Authorization; +using Volo.Abp.Dapr; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.Dapr; + +public class DaprAppApiTokenValidator : IDaprAppApiTokenValidator, ISingletonDependency +{ + public virtual void CheckDaprAppApiToken(HttpContext httpContext) + { + var expectedAppApiToken = GetConfiguredAppApiToken(httpContext); + if (expectedAppApiToken.IsNullOrWhiteSpace()) + { + return; + } + + var headerAppApiToken = GetDaprAppApiTokenOrNull(httpContext); + if (headerAppApiToken.IsNullOrWhiteSpace()) + { + throw new AbpAuthorizationException("Expected Dapr App API Token is not provided! Dapr should set the 'dapr-api-token' HTTP header."); + } + + if (expectedAppApiToken != headerAppApiToken) + { + throw new AbpAuthorizationException("The Dapr App API Token (provided in the 'dapr-api-token' HTTP header) doesn't match the expected value!"); + } + } + + public virtual bool IsValidDaprAppApiToken(HttpContext httpContext) + { + var expectedAppApiToken = GetConfiguredAppApiToken(httpContext); + if (expectedAppApiToken.IsNullOrWhiteSpace()) + { + return true; + } + + var headerAppApiToken = GetDaprAppApiTokenOrNull(httpContext); + return expectedAppApiToken == headerAppApiToken; + } + + public virtual string? GetDaprAppApiTokenOrNull(HttpContext httpContext) + { + string apiTokenHeader = httpContext.Request.Headers["dapr-api-token"]; + if (string.IsNullOrEmpty(apiTokenHeader) || apiTokenHeader.Length < 1) + { + return null; + } + + return apiTokenHeader; + } + + protected virtual string GetConfiguredAppApiToken(HttpContext httpContext) + { + var expectedAppApiToken = httpContext + .RequestServices + .GetRequiredService() + .GetAppApiToken(); + return expectedAppApiToken; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs new file mode 100644 index 00000000000..a35fbef2dc1 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.Dapr; + +public static class DaprHttpContextExtensions +{ + public static void ValidateDaprAppApiToken(this HttpContext httpContext) + { + httpContext + .RequestServices + .GetRequiredService() + .CheckDaprAppApiToken(httpContext); + } + + public static bool IsValidDaprAppApiToken(this HttpContext httpContext) + { + return httpContext + .RequestServices + .GetRequiredService() + .IsValidDaprAppApiToken(httpContext); + } + + public static string? GetDaprAppApiTokenOrNull(HttpContext httpContext) + { + return httpContext + .RequestServices + .GetRequiredService() + .GetDaprAppApiTokenOrNull(httpContext); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs new file mode 100644 index 00000000000..b3de839fc58 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Http; + +namespace Volo.Abp.AspNetCore.Mvc.Dapr; + +public interface IDaprAppApiTokenValidator +{ + void CheckDaprAppApiToken(HttpContext httpContext); + bool IsValidDaprAppApiToken(HttpContext httpContext); + string? GetDaprAppApiTokenOrNull(HttpContext httpContext); +} \ No newline at end of file From a24baee639fcb3244df0cb98cc18b525ba90cba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 10:09:15 +0300 Subject: [PATCH 14/21] Refactor DaprAppApiTokenValidator --- .../Mvc/Dapr/DaprAppApiTokenValidator.cs | 35 +++++++++++++------ .../Mvc/Dapr/DaprHttpContextExtensions.cs | 6 ++-- .../Mvc/Dapr/IDaprAppApiTokenValidator.cs | 8 ++--- .../Volo/Abp/Dapr/DaprApiTokenProvider.cs | 4 +-- .../Volo/Abp/Dapr/IDaprApiTokenProvider.cs | 4 +-- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs index aebfabeecd0..0b6e9a03b9f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization; using Volo.Abp.Dapr; @@ -8,15 +9,23 @@ namespace Volo.Abp.AspNetCore.Mvc.Dapr; public class DaprAppApiTokenValidator : IDaprAppApiTokenValidator, ISingletonDependency { - public virtual void CheckDaprAppApiToken(HttpContext httpContext) + protected IHttpContextAccessor HttpContextAccessor { get; } + protected HttpContext HttpContext => GetHttpContext(); + + public DaprAppApiTokenValidator(IHttpContextAccessor httpContextAccessor) + { + HttpContextAccessor = httpContextAccessor; + } + + public virtual void CheckDaprAppApiToken() { - var expectedAppApiToken = GetConfiguredAppApiToken(httpContext); + var expectedAppApiToken = GetConfiguredAppApiToken(); if (expectedAppApiToken.IsNullOrWhiteSpace()) { return; } - var headerAppApiToken = GetDaprAppApiTokenOrNull(httpContext); + var headerAppApiToken = GetDaprAppApiTokenOrNull(); if (headerAppApiToken.IsNullOrWhiteSpace()) { throw new AbpAuthorizationException("Expected Dapr App API Token is not provided! Dapr should set the 'dapr-api-token' HTTP header."); @@ -28,21 +37,21 @@ public virtual void CheckDaprAppApiToken(HttpContext httpContext) } } - public virtual bool IsValidDaprAppApiToken(HttpContext httpContext) + public virtual bool IsValidDaprAppApiToken() { - var expectedAppApiToken = GetConfiguredAppApiToken(httpContext); + var expectedAppApiToken = GetConfiguredAppApiToken(); if (expectedAppApiToken.IsNullOrWhiteSpace()) { return true; } - var headerAppApiToken = GetDaprAppApiTokenOrNull(httpContext); + var headerAppApiToken = GetDaprAppApiTokenOrNull(); return expectedAppApiToken == headerAppApiToken; } - public virtual string? GetDaprAppApiTokenOrNull(HttpContext httpContext) + public virtual string? GetDaprAppApiTokenOrNull() { - string apiTokenHeader = httpContext.Request.Headers["dapr-api-token"]; + string apiTokenHeader = HttpContext.Request.Headers["dapr-api-token"]; if (string.IsNullOrEmpty(apiTokenHeader) || apiTokenHeader.Length < 1) { return null; @@ -51,12 +60,16 @@ public virtual bool IsValidDaprAppApiToken(HttpContext httpContext) return apiTokenHeader; } - protected virtual string GetConfiguredAppApiToken(HttpContext httpContext) + protected virtual string GetConfiguredAppApiToken() { - var expectedAppApiToken = httpContext + return HttpContext .RequestServices .GetRequiredService() .GetAppApiToken(); - return expectedAppApiToken; + } + + protected virtual HttpContext GetHttpContext() + { + return HttpContextAccessor.HttpContext ?? throw new AbpException("HttpContext is not available!"); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs index a35fbef2dc1..15663d0c3ca 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprHttpContextExtensions.cs @@ -10,7 +10,7 @@ public static void ValidateDaprAppApiToken(this HttpContext httpContext) httpContext .RequestServices .GetRequiredService() - .CheckDaprAppApiToken(httpContext); + .CheckDaprAppApiToken(); } public static bool IsValidDaprAppApiToken(this HttpContext httpContext) @@ -18,7 +18,7 @@ public static bool IsValidDaprAppApiToken(this HttpContext httpContext) return httpContext .RequestServices .GetRequiredService() - .IsValidDaprAppApiToken(httpContext); + .IsValidDaprAppApiToken(); } public static string? GetDaprAppApiTokenOrNull(HttpContext httpContext) @@ -26,6 +26,6 @@ public static bool IsValidDaprAppApiToken(this HttpContext httpContext) return httpContext .RequestServices .GetRequiredService() - .GetDaprAppApiTokenOrNull(httpContext); + .GetDaprAppApiTokenOrNull(); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs index b3de839fc58..ed5f281ea81 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/IDaprAppApiTokenValidator.cs @@ -1,10 +1,8 @@ -using Microsoft.AspNetCore.Http; - namespace Volo.Abp.AspNetCore.Mvc.Dapr; public interface IDaprAppApiTokenValidator { - void CheckDaprAppApiToken(HttpContext httpContext); - bool IsValidDaprAppApiToken(HttpContext httpContext); - string? GetDaprAppApiTokenOrNull(HttpContext httpContext); + void CheckDaprAppApiToken(); + bool IsValidDaprAppApiToken(); + string? GetDaprAppApiTokenOrNull(); } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs index 326d4466756..9fbed78204e 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/DaprApiTokenProvider.cs @@ -12,12 +12,12 @@ public DaprApiTokenProvider(IOptions options) Options = options.Value; } - public virtual string GetDaprApiToken() + public virtual string? GetDaprApiToken() { return Options.DaprApiToken; } - public virtual string GetAppApiToken() + public virtual string? GetAppApiToken() { return Options.AppApiToken; } diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs index 260b0ea7091..8366eee8f71 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs @@ -2,6 +2,6 @@ namespace Volo.Abp.Dapr; public interface IDaprApiTokenProvider { - string GetDaprApiToken(); - string GetAppApiToken(); + string? GetDaprApiToken(); + string? GetAppApiToken(); } \ No newline at end of file From bbfe522623498da5f7431f354f7cc3be78f3ca16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 10:37:43 +0300 Subject: [PATCH 15/21] Complete the initial distributed eventbus doc for dapr --- docs/en/Dapr/Index.md | 105 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index 669fa070a62..22c0b00f649 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -65,7 +65,7 @@ Alternatively, you can configure the options in the `Dapr` section of your `apps **Example usages:** ````csharp -public class MyService +public class MyService : ITransientDependency { private readonly IAbpDaprClientFactory _daprClientFactory; @@ -136,7 +136,7 @@ The remove service name (`Default` in this example) should match the remote serv ## Distributed Event Bus Integration -[ABP's distributed event bus](../Distributed-Event-Bus.md) system provides a convenient abstraction to allow application communicate asynchronously via events. ABP has integration packages with various distributed messaging systems, like RabbitMQ, Kafka, and Azure. Dapr also has a [publish & subscribe building block](https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-overview/) for the same purpose: distributed messaging / events. +[ABP's distributed event bus](../Distributed-Event-Bus.md) system provides a convenient abstraction to allow applications communicate asynchronously via events. ABP has integration packages with various distributed messaging systems, like RabbitMQ, Kafka, and Azure. Dapr also has a [publish & subscribe building block](https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-overview/) for the same purpose: distributed messaging / events. ABP's [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Dapr) and [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) packages make possible to use the Dapr infrastructure for ABP's distributed event bus. @@ -146,8 +146,6 @@ The [Volo.Abp.EventBus.Dapr](https://www.nuget.org/packages/Volo.Abp.EventBus.Da If your application is an ASP.NET Core application and you want to send and receive events, you need to install the [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) package as describe below: -You can use the ABP CLI to add [Volo.Abp.AspNetCore.Mvc.Dapr.EventBus](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc.Dapr.EventBus) NuGet package to your project (to the client side): - * Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. * Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.AspNetCore.Mvc.Dapr.EventBus` package. * Run `abp add-package Volo.Abp.AspNetCore.Mvc.Dapr.EventBus` command. @@ -181,11 +179,104 @@ Available properties of the `AbpDaprEventBusOptions` class: ### Usage -You can follow [ABP's distributed event bus documentation](../Distributed-Event-Bus.md) to learn how to publish and subscribe to events in the ABP way. No change required in your application code to use Dapr pub-sub. +#### The ABP Way + +You can follow [ABP's distributed event bus documentation](../Distributed-Event-Bus.md) to learn how to publish and subscribe to events in the ABP way. No change required in your application code to use Dapr pub-sub. ABP will automatically subscribe to Dapr for your event handler classes (those implement the `IDistributedEventHandler` interface). + +**Example: Publish an event using the `IDistributedEventBus` service** + +````csharp +public class MyService : ITransientDependency +{ + private readonly IDistributedEventBus _distributedEventBus; + + public MyService(IDistributedEventBus distributedEventBus) + { + _distributedEventBus = distributedEventBus; + } + + public async Task DoItAsync() + { + await _distributedEventBus.PublishAsync(new StockCountChangedEto + { + ProductCode = "AT837234", + NewStockCount = 42 + }); + } +} +```` + +**Example: Subscribe to an event by implementing the `IDistributedEventHandler` interface** + +````csharp +public class MyHandler : + IDistributedEventHandler, + ITransientDependency +{ + public async Task HandleEventAsync(StockCountChangedEto eventData) + { + var productCode = eventData.ProductCode; + // ... + } +} +```` + +See [ABP's distributed event bus documentation](../Distributed-Event-Bus.md) to learn the details. + +#### Using the Dapr API + +In addition to ABP's standard distributed event bus system, you can also use Dapr's API to publish events. + +> If you directly use the Dapr API to publish events, you may not benefit from ABP's standard distributed event bus features, like the outbox/inbox pattern implementation. + +**Example: Publish an event using `DaprClient`** + +````csharp +public class MyService : ITransientDependency +{ + private readonly DaprClient _daprClient; + + public MyService(DaprClient daprClient) + { + _daprClient = daprClient; + } + + public async Task DoItAsync() + { + await _daprClient.PublishEventAsync( + "pubsub", // pubsub name + "StockChanged", // topic name + new StockCountChangedEto // event data + { + ProductCode = "AT837234", + NewStockCount = 42 + } + ); + } +} +```` + +**Example: Subscribe to an event by creating an ASP.NET Core controller** + +````csharp +public class MyController : AbpController +{ + [HttpPost("/stock-changed")] + [Topic("pubsub", "StockChanged")] + public async Task TestRouteAsync( + [FromBody] StockCountChangedEto model) + { + HttpContext.ValidateDaprAppApiToken(); + + // Do something with the event + return Ok(); + } +} +```` -In addition to ABP's standard distributed event bus system, you can also use Dapr's API to publish events. In that case, just use the `IAbpDaprClientFactory` service to create a `DaprClient` object (or create it yourself by following Dapr's documentation) and use its `PublishEventAsync` method as [documented by Dapr](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/publish-subscribe). +`HttpContext.ValidateDaprAppApiToken()` extension method is provided by ABP to check if the request is coming from Dapr. This is optional. You should configure Dapr to send App API token to your application if you want to enable the validation. If not configured, `ValidateDaprAppApiToken()` does nothing. See [Dapr's App API Token document](https://docs.dapr.io/operations/security/app-api-token/) for more information. Also see the *AbpDaprOptions* section in this document. -> If you directly use the Dapr API to publish events, you can not benefit from ABP's standard distributed event bus features, like the outbox/inbox pattern implementation. +See the [Dapr documentation](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/publish-subscribe) to learn the details of sending & receiving events with Dapr API. ## See Also From 8aa4790d1ff24a12424ef53c49bc5ac38896e7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 11:15:29 +0300 Subject: [PATCH 16/21] Added security section and refactored. --- docs/en/Dapr/Index.md | 83 ++++++++++++++++++- .../Mvc/Dapr/DaprAppApiTokenValidator.cs | 6 +- .../Volo/Abp/Dapr/IDaprApiTokenProvider.cs | 1 + 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index 22c0b00f649..a5b6dd99f92 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -49,6 +49,8 @@ Available properties of the `AbpDaprOptions` class: * `HttpEndpoint` (optional): HTTP endpoint that is used while creating a `DaprClient` object. If you don't specify, the default value is used. * `GrpcEndpoint` (optional): The gRPC endpoint that is used while creating a `DaprClient` object. If you don't specify, the default value is used. +* `DaprApiToken` (optional): The [Dapr API token](https://docs.dapr.io/operations/security/api-token/) that is used while sending requests from the application to Dapr. It is filled from the `DAPR_API_TOKEN` environment variable by default (which is set by Dapr once it is configured). See the *Security* section in this document for details. +* `AppApiToken` (optional): The [App API token](https://docs.dapr.io/operations/security/app-api-token/) that is used to validate requests coming from Dapr. It is filled from the `APP_API_TOKEN` environment variable by default (which is set by Dapr once it is configured). See the *Security* section in this document for details. Alternatively, you can configure the options in the `Dapr` section of your `appsettings.json` file. Example: @@ -274,10 +276,89 @@ public class MyController : AbpController } ```` -`HttpContext.ValidateDaprAppApiToken()` extension method is provided by ABP to check if the request is coming from Dapr. This is optional. You should configure Dapr to send App API token to your application if you want to enable the validation. If not configured, `ValidateDaprAppApiToken()` does nothing. See [Dapr's App API Token document](https://docs.dapr.io/operations/security/app-api-token/) for more information. Also see the *AbpDaprOptions* section in this document. +`HttpContext.ValidateDaprAppApiToken()` extension method is provided by ABP to check if the request is coming from Dapr. This is optional. You should configure Dapr to send App API token to your application if you want to enable the validation. If not configured, `ValidateDaprAppApiToken()` does nothing. See [Dapr's App API Token document](https://docs.dapr.io/operations/security/app-api-token/) for more information. Also see the *AbpDaprOptions* and *Security* sections in this document. See the [Dapr documentation](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/publish-subscribe) to learn the details of sending & receiving events with Dapr API. +## Security + +If you are using Dapr, most or all the incoming and outgoing requests in your application pass through Dapr. Dapr uses two kinds of API tokens to secure the communication between your application and Dapr. + +### Dapr API Token + +> This token is automatically set by default and generally you don't care about it. + +The [Enable API token authentication in Dapr](https://docs.dapr.io/operations/security/api-token/) document describes what the Dapr API token is and how it is configured. Please read that document if you want to enable it for your application. + +If you enabled the Dapr API token, you should send that token in every request to Dapr from your application. `AbpDaprOptions` defines a `DaprApiToken` property as a central point to configure the Dapr API token in your application. + +The default value of the `DaprApiToken` property is set from the `DAPR_API_TOKEN` environment variable and that environment variable is set by Dapr when it runs. So, most of the times, you don't need to configure `AbpDaprOptions.DaprApiToken` in your application. However, if you need to configure (or override) it, you can do in the `ConfigureServices` method of your module class as shown in the following code block: + +````csharp +Configure(options => +{ + options.DaprApiToken = "..."; +}); +```` + +Or you can set it in your `appsettings.json` file: + +````json +"Dapr": { + "DaprApiToken": "..." +} +```` + +Once you set it, it is used when you inject `DaprClient` or use `IAbpDaprClientFactory`. If you need that value in your application, you can inject `IDaprApiTokenProvider` and use its `GetDaprApiToken()` method. + +### App API Token + +> Enabling App API token validation is strongly recommended. Otherwise, for example, any client can directly call your event subscription endpoint, and your application acts like an event has occurred (if there is no other security policy in your event subscription endpoint). + +The [Authenticate requests from Dapr using token authentication](https://docs.dapr.io/operations/security/app-api-token/) document describes what the App API token is and how it is configured. Please read that document if you want to enable it for your application. + +If you enabled the App API token, you can validate it to ensure that the request is coming from Dapr. ABP provides useful shortcuts to validate it. + +**Example: Validate the App API token in an event handling HTTP API** + +````csharp +public class MyController : AbpController +{ + [HttpPost("/stock-changed")] + [Topic("pubsub", "StockChanged")] + public async Task TestRouteAsync( + [FromBody] StockCountChangedEto model) + { + // Validate the App API token! + HttpContext.ValidateDaprAppApiToken(); + + // Do something with the event + return Ok(); + } +} +```` + +`HttpContext.ValidateDaprAppApiToken()` is an extension method provided by the ABP Framework. It throws an `AbpAuthorizationException` if the token was missing or wrong in the HTTP header (the header name is `dapr-api-token`). You can also inject `IDaprAppApiTokenValidator` and use its methods to validate the token in any service (not only in a controller class). + +You can configure `AbpDaprOptions.AppApiToken` if you want to set (or override) the App API token value. The default value is set by the `APP_API_TOKEN` environment variable. You can change it in the `ConfigureServices` method of your module class as shown in the following code block: + +````csharp +Configure(options => +{ + options.AppApiToken = "..."; +}); +```` + +Or you can set it in your `appsettings.json` file: + +````json +"Dapr": { + "AppApiToken": "..." +} +```` + +Once + ## See Also * [Dapr for .NET Developers](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs index 0b6e9a03b9f..817eb9824d8 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Dapr/Volo/Abp/AspNetCore/Mvc/Dapr/DaprAppApiTokenValidator.cs @@ -19,7 +19,7 @@ public DaprAppApiTokenValidator(IHttpContextAccessor httpContextAccessor) public virtual void CheckDaprAppApiToken() { - var expectedAppApiToken = GetConfiguredAppApiToken(); + var expectedAppApiToken = GetConfiguredAppApiTokenOrNull(); if (expectedAppApiToken.IsNullOrWhiteSpace()) { return; @@ -39,7 +39,7 @@ public virtual void CheckDaprAppApiToken() public virtual bool IsValidDaprAppApiToken() { - var expectedAppApiToken = GetConfiguredAppApiToken(); + var expectedAppApiToken = GetConfiguredAppApiTokenOrNull(); if (expectedAppApiToken.IsNullOrWhiteSpace()) { return true; @@ -60,7 +60,7 @@ public virtual bool IsValidDaprAppApiToken() return apiTokenHeader; } - protected virtual string GetConfiguredAppApiToken() + protected virtual string? GetConfiguredAppApiTokenOrNull() { return HttpContext .RequestServices diff --git a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs index 8366eee8f71..89ea4d516dc 100644 --- a/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs +++ b/framework/src/Volo.Abp.Dapr/Volo/Abp/Dapr/IDaprApiTokenProvider.cs @@ -3,5 +3,6 @@ namespace Volo.Abp.Dapr; public interface IDaprApiTokenProvider { string? GetDaprApiToken(); + string? GetAppApiToken(); } \ No newline at end of file From 22a33e6ac3f1e1b548d917287d33e2dfce6c4f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 11:20:54 +0300 Subject: [PATCH 17/21] Add section: Injecting DaprClient --- docs/en/Dapr/Index.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index a5b6dd99f92..7c6b5f75b88 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -60,9 +60,32 @@ Alternatively, you can configure the options in the `Dapr` section of your `apps } ```` +### Injecting DaprClient + +ABP registers the `DaprClient` class to the [dependency injection](../Dependency-Injection.md) system. So, you can inject and use it whenever you need: + +````csharp +public class MyService : ITransientDependency +{ + private readonly DaprClient _daprClient; + + public MyService(DaprClient daprClient) + { + _daprClient = daprClient; + } + + public async Task DoItAsync() + { + // TODO: Use the injected _daprClient object + } +} +```` + +Injecting `DaprClient` is the recommended way of using it in your application code. When you inject it, the `IAbpDaprClientFactory` service is used to create it, which is explained in the next section. + ### IAbpDaprClientFactory -`IAbpDaprClientFactory` is used to create `DaprClient` or `HttpClient` objects to perform operations on Dapr. It uses `AbpDaprOptions`, so you can configure the settings in a central place. +`IAbpDaprClientFactory` can be used to create `DaprClient` or `HttpClient` objects to perform operations on Dapr. It uses `AbpDaprOptions`, so you can configure the settings in a central place. **Example usages:** From d1983e680a38d12156a1e6c5abd0c6d7ae25d0a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 11:48:11 +0300 Subject: [PATCH 18/21] Update Index.md --- docs/en/Dapr/Index.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index 7c6b5f75b88..e6acf7a9745 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -194,7 +194,7 @@ You can configure the `AbpDaprEventBusOptions` [options class](../Options.md) fo ````csharp Configure(options => { - options.PubSubName = "test-pubsub"; + options.PubSubName = "pubsub"; }); ```` @@ -202,12 +202,23 @@ Available properties of the `AbpDaprEventBusOptions` class: * `PubSubName` (optional): The `pubsubName` parameter while publishing messages through `DaprClient.PublishEventAsync` method. Default value: `pubsub`. +### The ABP Subscription Endpoints + +ABP provides the following endpoints to receive events from Dapr: + +* `dapr/subscribe`: Dapr uses this endpoint to get a list of subscriptions from the application. ABP automatically returns all the subscriptions for your distributed event handler classes and custom controller actions with the `Topic` attribute. +* `api/abp/dapr/event`: The unified endpoint to receive all the events from Dapr. ABP dispatches the events to your event handlers based on the topic name. + +> **Since ABP provides the standard `dapr/subscribe` endpoint, you should not manually call the `app.MapSubscribeHandler()` method of Dapr.** You can use the `app.UseCloudEvents()` middleware in your ASP.NET Core pipeline if you want to support the [CloudEvents](https://cloudevents.io/) standard. + ### Usage #### The ABP Way You can follow [ABP's distributed event bus documentation](../Distributed-Event-Bus.md) to learn how to publish and subscribe to events in the ABP way. No change required in your application code to use Dapr pub-sub. ABP will automatically subscribe to Dapr for your event handler classes (those implement the `IDistributedEventHandler` interface). +ABP provides `api/abp/dapr/event` + **Example: Publish an event using the `IDistributedEventBus` service** ````csharp @@ -380,7 +391,7 @@ Or you can set it in your `appsettings.json` file: } ```` -Once +If you need that value in your application, you can inject `IDaprApiTokenProvider` and use its `GetAppApiToken()` method. ## See Also From 1e75e6c1a7ba16ce3561475fb44836f66aac380e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 13:44:01 +0300 Subject: [PATCH 19/21] Change DaprAbpDistributedLock implementation --- .../Volo/Abp/DistributedLocking/IAbpDistributedLock.cs | 2 +- .../Dapr/AbpDistributedLockDaprOptions.cs | 4 ++-- .../Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs | 7 +------ 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IAbpDistributedLock.cs index 760021c90a0..754ae807f8d 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Abstractions/Volo/Abp/DistributedLocking/IAbpDistributedLock.cs @@ -14,7 +14,7 @@ public interface IAbpDistributedLock /// Returns null if the lock could not be handled. /// /// The name of the lock - /// Timeout value + /// How long to wait before giving up on the acquisition attempt. Defaults to 0 /// Cancellation token [ItemCanBeNull] Task TryAcquireAsync( diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs index c2688abd1b7..75797968711 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/AbpDistributedLockDaprOptions.cs @@ -6,10 +6,10 @@ public class AbpDistributedLockDaprOptions public string? Owner { get; set; } - public TimeSpan DefaultTimeout { get; set; } + public TimeSpan DefaultExpirationTimeout { get; set; } public AbpDistributedLockDaprOptions() { - DefaultTimeout = TimeSpan.FromSeconds(30); + DefaultExpirationTimeout = TimeSpan.FromMinutes(2); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs index d055376fb8a..ef99b522273 100644 --- a/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs +++ b/framework/src/Volo.Abp.DistributedLocking.Dapr/Volo/Abp/DistributedLocking/Dapr/DaprAbpDistributedLock.cs @@ -26,11 +26,6 @@ public DaprAbpDistributedLock( TimeSpan timeout = default, CancellationToken cancellationToken = default) { - if (timeout == default) - { - timeout = DistributedLockDaprOptions.DefaultTimeout; - } - name = DistributedLockKeyNormalizer.NormalizeKey(name); var daprClient = DaprClientFactory.Create(); @@ -38,7 +33,7 @@ public DaprAbpDistributedLock( DistributedLockDaprOptions.StoreName, name, DistributedLockDaprOptions.Owner ?? Guid.NewGuid().ToString(), - (int)timeout.TotalSeconds, + (int)DistributedLockDaprOptions.DefaultExpirationTimeout.TotalSeconds, cancellationToken); if (lockResponse == null || !lockResponse.Success) From 8d83ef476131ad0dbd72b1adc89737041930c414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 13:44:13 +0300 Subject: [PATCH 20/21] Complete the Dapr documentation. --- docs/en/Dapr/Index.md | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/en/Dapr/Index.md b/docs/en/Dapr/Index.md index e6acf7a9745..4cdf28c7f9f 100644 --- a/docs/en/Dapr/Index.md +++ b/docs/en/Dapr/Index.md @@ -314,6 +314,76 @@ public class MyController : AbpController See the [Dapr documentation](https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/publish-subscribe) to learn the details of sending & receiving events with Dapr API. +## Distributed Lock + +> Dapr's distributed lock feature currently in Alpha stage and may not be stable yet. It is not suggested to replace ABP's distributed lock with Dapr in that point. + +ABP provides a [Distributed Locking](../Distributed-Locking.md) abstraction to control access to a shared resource by multiple applications. Dapr also has a [distributed lock building block](https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/). [Volo.Abp.DistributedLocking.Dapr](https://www.nuget.org/packages/Volo.Abp.DistributedLocking.Dapr) package makes ABP using Dapr's distributed locking system. + +### Installation + +Use the ABP CLI to add [Volo.Abp.DistributedLocking.Dapr](https://www.nuget.org/packages/Volo.Abp.DistributedLocking.Dapr) NuGet package to your project (to the client side): + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.DistributedLocking.Dapr` package. +* Run `abp add-package Volo.Abp.DistributedLocking.Dapr` command. + +If you want to do it manually, install the [Volo.Abp.DistributedLocking.Dapr](https://www.nuget.org/packages/Volo.Abp.DistributedLocking.Dapr) NuGet package to your project and add `[DependsOn(typeof(AbpDistributedLockingDaprModule))]` to the [ABP module](../Module-Development-Basics.md) class inside your project. + +### Configuration + +You can use the `AbpDistributedLockDaprOptions` options class in the `ConfigureServices` method of [your module](../Module-Development-Basics.md) to configure the Dapr distributed lock: + +````csharp +Configure(options => +{ + options.StoreName = "mystore"; +}); +```` + +The following options are available: + +* **`StoreName`** (required): The store name used by Dapr. Lock key names are scoped in the same store. That means different applications can acquire the same lock name in different stores. Use the same store name for the same resources you want to control access. +* `Owner` (optional): The `owner` value used by `DaprClient.Lock` method. If you don't specify, ABP uses a random value, which is fine in general. +* `DefaultExpirationTimeout` (optional): Default value of the time after which the lock gets expired. Default value: 2 minutes. + +### Usage + +You can inject and use the `IAbpDistributedLock` service, just like explained in the [Distributed Locking document](../Distributed-Locking.md). + +**Example:** + +````csharp +public class MyService : ITransientDependency +{ + private readonly IAbpDistributedLock _distributedLock; + + public MyService(IAbpDistributedLock distributedLock) + { + _distributedLock = distributedLock; + } + + public async Task MyMethodAsync() + { + await using (var handle = + await _distributedLock.TryAcquireAsync("MyLockName")) + { + if (handle != null) + { + // your code that access the shared resource + } + } + } +} +```` + +There are two points we should mention about the `TryAcquireAsync` method, as different from the ABP's standard usage: + +* `timeout` parameter currently not used (even if you specify it), because Dapr doesn't support to wait to obtain the lock. +* Dapr uses expiration timeout system (that means the lock is automatically released after that timeout even if you don't release the lock by disposing the handler). However, ABP's `TryAcquireAsync` method has no such a parameter. Currently, you can set `AbpDistributedLockDaprOptions.DefaultExpirationTimeout` as a global value in your application. + +As mentioned first, Dapr's distributed lock feature currently in Alpha stage and its API is candidate to change. You should use it as is if you want, but be ready for the changes in the future. For now, we are recommending to use the [DistributedLock](https://github.com/madelson/DistributedLock) library as explained in ABP's [Distributed Locking document](../Distributed-Locking.md). + ## Security If you are using Dapr, most or all the incoming and outgoing requests in your application pass through Dapr. Dapr uses two kinds of API tokens to secure the communication between your application and Dapr. From ea17fbf5db05d4a685757af30b649be4abf0d630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 15 Sep 2022 13:52:21 +0300 Subject: [PATCH 21/21] Added Dapr Integration to main menu --- docs/en/docs-nav.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 035f150705d..ed9cb37135b 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -1283,6 +1283,10 @@ } ] }, + { + "text": "Dapr Integration", + "path": "Dapr/Index.md" + }, { "text": "Testing", "path": "Testing.md"