Merge branch 'dev' into localization-v7

pull/13845/head
Halil İbrahim Kalkan 3 years ago
commit 98414520a2

@ -2,8 +2,12 @@ name: Pull request labeler
on:
schedule:
- cron: '0 12 */1 * *'
permissions:
contents: read
jobs:
labeler:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: paulfantom/periodic-labeler@master

@ -177,6 +177,8 @@
"CheckAllCommunityTalks": "Check All Community Posts",
"ReadMore": "Read More",
"Post": "Post",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community."
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "Explore the contents created by the core ABP team and the ABP community.",
"WelcomeFallCampaign": "Welcome Fall Campaign!",
"GiveAwayForNewPurchases": "Application Development Classroom Training will be given away for the new purchases!"
}
}

@ -39,6 +39,8 @@
"TrialLicensePeriodHasExpired": "Deneme lisansınızın süresi {0} gün önce sona erdi.",
"TrialLicensePeriodWillExpire": "Deneme lisansınızın süresi {0} gün içinde dolacak.",
"TrialLicensePeriodExpireToday": "Deneme lisans süreniz bugün sona erecek.",
"PurchaseNow": "Şimdi satın al!"
"PurchaseNow": "Şimdi satın al!",
"WelcomeFallCampaign": "Hoş Geldin Sonbahar Kampanyası!",
"GiveAwayForNewPurchases": "Yeni alımlar için Uygulama Geliştirme Sınıfı Eğitimi hediye edilecektir!"
}
}

@ -174,6 +174,8 @@
"CheckAllCommunityTalks": "检查所有社区帖子",
"ReadMore": "阅读更多",
"Post": "邮政",
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。"
"ExploreTheContentsCreatedByTheCoreABPTeamAndTheABPCommunity": "探索核心 ABP 团队和 ABP 社区创建的内容。",
"WelcomeFallCampaign": "欢迎秋季活动!",
"GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
}
}

@ -39,6 +39,8 @@
"TrialLicensePeriodHasExpired": "您的試用許可期限已於 {0} 天前到期。",
"TrialLicensePeriodWillExpire": "您的試用許可期限將在 {0} 天后到期。",
"TrialLicensePeriodExpireToday": "您的試用許可期將於今天到期。",
"PurchaseNow": "現在買!"
"PurchaseNow": "現在買!",
"WelcomeFallCampaign": "欢迎秋季活动!",
"GiveAwayForNewPurchases": "新购买将赠送应用程序开发课堂培训!"
}
}

@ -38,5 +38,6 @@
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dapr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

@ -23,13 +23,13 @@ https://github.com/abpframework/abp/tree/master/modules/docs
You can download the VoloDocs release from the following links:
http://apps.abp.io/VoloDocs/VoloDocs.win-x64.zip - **Windows 64 bit**
https://apps.abp.io/VoloDocs/VoloDocs.win-x64.zip - **Windows 64 bit**
http://apps.abp.io/VoloDocs/VoloDocs.win-x86.zip - **Windows 32 bit**
https://apps.abp.io/VoloDocs/VoloDocs.win-x86.zip - **Windows 32 bit**
http://apps.abp.io/VoloDocs/VoloDocs.osx-x64.zip - **MacOS**
https://apps.abp.io/VoloDocs/VoloDocs.osx-x64.zip - **MacOS**
http://apps.abp.io/VoloDocs/VoloDocs.linux-x64.zip - **Linux**
https://apps.abp.io/VoloDocs/VoloDocs.linux-x64.zip - **Linux**
Notice that, all installations are self-contained deployments. It means all the required third-party dependencies along with the version of .NET Core is included. So you don't need to install any .NET Core SDK / Runtime.

@ -244,25 +244,35 @@ One restriction of property injection is that you cannot use the dependency in y
Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class.
### Resolve Service from IServiceProvider
#### DisablePropertyInjectionAttribute
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties.
````C#
[DisablePropertyInjection]
public class MyService : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public ITaxCalculator TaxCalculator { get; set; }
}
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public void DoSomething()
{
var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
//...
}
[DisablePropertyInjection]
public ITaxCalculator TaxCalculator { get; set; }
}
````
### Resolve Service from IServiceProvider
You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below:
````C#
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
}
````

@ -1,425 +0,0 @@
# Deploy Abp Webapp to Azure App Service
> In this document, you'll learn how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview). The App Service supports various versions of .NET apps, and provides a highly scalable, self-patching web hosting service. Abp web apps are cross-platform and can be hosted on Linux and Windows.
****Prerequisites****
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/dotnet).
- A GitHub account [Create an account for free](http://github.com/).
## Create a new ABP Framework application
Create a repository on [GitHub.com](https://github.com/) (keep all the default settings)
Open the command prompt and clone the repository into a folder on your computer
```bash
git clone https://github.com/your-username/your-repository-name.git
```
Check your dotnet version. It should be at least 3.1.x
```bash
dotnet --version
```
Install or update the *ABP CLI* using a command line window
```bash
dotnet tool install -g Volo.Abp.Cli || dotnet tool update -g Volo.Abp.Cli
```
Open the command prompt in the *GitHub repository folder* and create a *new abp Blazor solution* with the command below
```bash
abp new YourAppName -u blazor
```
Open the command prompt in the *[YourAppName].DbMigrator* project and enter the command below to apply the database migrations
```bash
dotnet run
```
Open the command prompt in the *[YourAppName].HttpApi.Host* project to run the API project
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in *the launchSettings.json* file of the *[YourAppName].HttpApi.Host project*. You should get the *Swagger window*
Open the command prompt in the *[YourAppName].Blazor* folder and enter the command below to run the Blazor project
```bash
dotnet run
```
Navigate to the *applicationUrl* specified in the *launchSettings.json* file of the *[YourAppName].Blazor* project. You should get the *ABP Framework Welcome window*
Stop both the *API* and the *Blazor* project by pressing **CTRL+C**
Before the github commit, you have to delete "**/wwwroot/libs/*" at *.gitignore* file.
![azdevops-23](../images/azdevops-23.png)
Open the command prompt in the root folder of your project and *add, commit and push* all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Create a SQL Database on Azure and change the connection string in the appsettings.json files
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *SQL Database*
* Click the **Create** button in the *SQL Database window*
* Create a new resource group. Name it *rg[YourAppName]*
* Enter *[YourAppName]Db* as database name
* Create a new Server and name it *[yourappname]server*
* Enter a serveradmin login and passwords. Click the **OK** button
* Select your *Location*
* Check *Allow Azure services to access server*
* Click **Configure database**. Go to the *Basic* version and click the **Apply** button
* Click the **Review + create** button. Click **Create**
* Click **Go to resource** and click **SQL server** when the SQL Database is created
* Click **Networking** under Security left side menu
* Select **Selected networks** and click **Add your client IP$ address** at the Firewall rules
* Select **Allow Azure and resources to access this seerver** and save
* Go to your **SQL database**, click **Connection strings** and copy the connection string
* Copy/paste the appsettings.json files of the [YourAppName].HttpApi.Host and the [YourAppName].DbMigrator project
* Do not forget to replace {your_password} with the correct server password you entered in Azure SQL Database
Open the command prompt in the [YourAppName].DbMigrator project again and enter the command below to apply the database migrations
```bash
dotnet run
```
Open the command prompt in the [YourAppName].HttpApi.Host project and enter the command below to check your API is working
```bash
dotnet run
```
Stop the [YourAppName].HttpApi.Host by entering CTRL+C
Open the command prompt in the root folder of your project and add, commit and push all your changes to your GitHub repository
```bash
git add .
git commit -m initialcommit
git push
```
## Set up the Build pipeline in AzureDevops and publish the Build Artifacts
* Sign in into Azure DevOps
* Click **New organization** and follow the steps to create a new organisation. Name it [YourAppName]org
* Enter [YourAppName]Proj as project name in the ***Create a project to get started*** window
* Select **Public visibility** and click the **Create project** button
* Click the **Pipelines** button to continue
* Click the **Create Pipeline** button
Select GitHub in the Select your repository window
![azdevops-1](../images/azdevops-1.png)
* Enter the Connection name. [YourAppName]GitHubConnection and click **Authorize using OAuth**
* Select your **GitHub** [YourAppName]repo and click Continue
* Search for **ASP.NET** in the ***Select a template*** window
![azdevops-2](../images/azdevops-2.png)
* Select the ASP.NET Core template and click the **Apply** button
* Add the below commands block as a first step in the pipeline
```
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
```
![azdevops-18](../images/azdevops-18.png)
* Select **Settings** on the second task(Nugetcommand@2) in the pipeline
* Select **Feeds in my Nuget.config** and type **Nuget.config** in the text box
![azdevops-3](../images/azdevops-3.png)
* Add the below commands block to the end of the pipeline
```
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
condition: succeededOrFailed()
```
![azdevops-4](../images/azdevops-4.png)
```
# ASP.NET
# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '6.0.106'
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'config'
nugetConfigPath: 'NuGet.config'
- task: VSBuild@1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: '$(Parameters.ArtifactName)'
publishLocation: 'Container'
condition: succeededOrFailed()
```
* Click **Save & queue** in the top menu. Click **Save & queue** again and click **Save and run** to run the Build pipeline
* When the Build pipeline has finished. Click **1 published; 1 consumed**
## Create a Web App in the Azure Portal to deploy [YourAppName].HttpApi.Host project
* Search for Web App in the *Search the Marketplace* field
* Click the **Create** button in the Web App window
* Select rg[YourAppName] in the *Resource Group* dropdown
* Enter [YourAppName]API in the *Name input* field
* Select code, .NET Core 3.1 (LTS) and windows as *Operating System*
* Enter [YourAppName]API in the *Name input* field
* Select .NET Core 3.1 (LTS) in the *Runtime stack* dropdown
* Select Windows as *Operating System*
* Select the same *Region* as in the SQL server you created in Part 3
![azdevops-5](../images/azdevops-5.png)
* Click **Create new** in the Windows Plan. Name it [YourAppName]ApiWinPlan
* Click **Change size** in Sku and size. Go to the Dev/Test Free F1 version and click the **Apply** button
![azdevops-6](../images/azdevops-6.png)
* Click the **Review + create** button. Click the **Create** button
* Click **Go to resource** when the Web App has been created
## Create a Release pipeline in the AzureDevops and deploy [YourAppName].HttpApi.Host project
* Sign in into [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)
* Click [YourAppName]Proj and click **Releases** in the *Pipelines* menu
* Click the **New pipeline** button in the *No release pipelines found* window
* Select *Azure App Service deployment* and click the **Apply** button
![azdevops-7](../images/azdevops-7.png)
* Enter *[YourAppName]staging* in the *Stage name* field in the *Stage* window. And close the window
* Click **+ Add an artifact** in the *Pipeline* tab
* Select the **Build** icon as *Source type* in the *Add an artifact* window
* Select Build pipeline in the *Source (build pipeline)* dropdown and click the **Add** button
![azdevops-8](../images/azdevops-8.png)
* Click the **Continuous deployment trigger (thunderbolt icon)**
* Set the toggle to **Enabled** in the the *Continuous deployment trigger* window
* Click **+ Add** in *No filters added*. Select **Include** in the *Type* dropdown. Select your branch in the *Build branch* dropdown and close the window
![azdevops-9](../images/azdevops-9.png)
* Click **the little red circle with the exclamation mark** in the *Tasks* tab menu
* Select your subscription in the *Azure subscription* dropdown.
![azdevops-10](../images/azdevops-10.png)
* Click **Authorize** and enter your credentials in the next screens
* After Authorization, select the **[YourAppName]API** in the *App service name* dropdown
* Click the **Deploy Azure App Service** task
* Select **[YourAppName].HttpApi.Host.zip** in the *Package or folder* input field
![azdevops-11](../images/azdevops-11.png)
* Click the **Save** icon in the top menu and click **OK**
* Click **Create release** in the top menu. Click **Create** to create a release
* Click the *Pipeline* tab and wait until the Deployment succeeds
![azdevops-12](../images/azdevops-12.png)
* Open a browser and navigate to the URL of your Web App
```
https://[YourAppName]api.azurewebsites.net
```
![azdevops-13](../images/azdevops-13.png)
## Create a Web App in Azure Portal to deploy [YourAppName].Blazor project
* Login into [Azure Portal](https://portal.azure.com/)
* Click **Create a resource**
* Search for *Web App* in the *Search the Marketplace* field
* Click the **Create** button in the *Web App* window
* Select *rg[YourAppName]* in the *Resource Group* dropdown
* Enter *[YourAppName]Blazor* in the *Name* input field
* Select *.NET Core 3.1 (LTS)* in the *Runtime stack* dropdown
* Select *Windows* as *Operating System*
* Select the same region as the SQL server you created in Part 3
* Select the [YourAppName]ApiWinPlan in the *Windows Plan* dropdown
![azdevops-14](../images/azdevops-14.png)
* Click the **Review + create** button. Click **Create** button
* Click **Go to resource** when the Web App has been created
* Copy the URL of the Blazor Web App for later use
```
https://[YourAppName]blazor.azurewebsites.net
```
## Change the Web App configuration for the Azure App Service
Copy the URL of the Api Host and Blazor Web App. Change appsettings.json files in the Web App as follows images.
![azdevops-19](../images/azdevops-19.png)
![azdevops-20](../images/azdevops-20.png)
![azdevops-21](../images/azdevops-21.png)
## Add an extra Stage in the Release pipeline in the AzureDevops to deploy [YourAppName].Blazor project
* Go to the *Release* pipeline in [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/) and click **Edit**
* Click the **+ Add** link and add a **New Stage**
![azdevops-15](../images/azdevops-15.png)
* Select *Azure App Service deployment* and click the **Apply** button
* Enter *BlazorDeployment* in the *Stage name* input field and close the *Stage* window
* Click the **little red circle with the exclamation mark** in the BlazorDeployment stage
* Select your subscription in the *Azure subscription* dropdown
* Select your Blazor Web App in the *App service name* dropdown
* Click the **Deploy Azure App Service task**
* Select *[YourAppName].Blazor.zip* in the *Package or folder* input field
![azdevops-16](../images/azdevops-16.png)
* Click **Save** in the top menu and click the **OK** button after
* Click **Create release** in the top menu and click the **Create** button
![azdevops-17](../images/azdevops-17.png)
![azdevops-22](../images/azdevops-22.png)

@ -7,6 +7,3 @@ However, there are some topics that you should care about when you are deploying
## Guides
* [Deploying to a clustered environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
* [Deploy Abp Webapp to Azure App Service](Deploy-Azure-App-Service.md): Explains how to create and deploy your first abp web app to [Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/overview).

@ -163,6 +163,8 @@ You can see the application APIs and test them here. Get [more info](https://swa
### Running the Blazor Application (Client Side)
Go to the Blazor project folder, open a command line terminal, type the `abp bundle -f` command (If the project was created by ABP Cli tool, you don't need to do this).
Ensure that the `.Blazor` project is the startup project and run the application.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.

@ -76,6 +76,8 @@ Use the `abp update` command to update your existing application. See [Upgrading
- Create a folder named *OpenIddict* under the Domain project and copy the [OpenIddictDataSeedContributor.cs](https://github.com/abpframework/abp-samples/blob/master/Ids2OpenId/src/Ids2OpenId.Domain/OpenIddict/OpenIddictDataSeedContributor.cs) under this folder. Rename all the `Ids2OpenId` with your project name.
- Delete *IdentityServer* folder that contains `IdentityServerDataSeedContributor.cs` which is no longer needed.
You can also create a project with the same name and copy the `OpenIddict` folder of the new project into your project.
### EntityFrameworkCore Layer
If you are using MongoDB, skip this step and check the *MongoDB* layer section.
@ -226,4 +228,4 @@ for creating the host builder.
## See Also
* [ABP Version 6.0 Migration Guide](Abp-6_0.md)
* [ABP Version 6.0 Migration Guide](Abp-6_0.md)

@ -1,102 +1,268 @@
## ABP OpenIddict Modules
## ABP OpenIddict Module
## User Interface
This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
OpenIddict module provides an integration with the [OpenIddict](https://github.com/openiddict/openiddict-core) which provides advanced authentication features like single sign-on, single log-out, and API access control. This module persists applications, scopes, and other OpenIddict-related objects to the database.
## How to Install
This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as a package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module.
### The Source Code
The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/openiddict). The source code is licensed by [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it.
## Relations to Other Modules
## User Interface
This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
This module implements the domain logic and database integrations but does not provide any UI. Management UI is useful if you need to add applications and scopes on the fly. In this case, you may build the management UI yourself or consider purchasing the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module.
## The module
## Relations to Other Modules
### Demo projects
This module is based on the [Identity Module](Identity.md) and has an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.OpenIddict) with the [Account Module](Account.md).
In the module's `app` directory there are six projects(including `angular`)
## Options
* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication
* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication
* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`
* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication
* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication
### OpenIddictBuilder
#### How to run?
`OpenIddictBuilder` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
Example:
### Domain module
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictBuilder>(builder =>
{
//Set options here...
});
}
```
There are four main entities included in this module.
`OpenIddictBuilder` contains various extension methods to configure the OpenIddict services:
* OpenIddictApplication: **Represents applications(client)**
* OpenIddictScope: **Represents scopes**
* OpenIddictAuthorization: **Represents authorizations, Track of logical chains of tokens and user consent..**
* OpenIddictToken: **Represents various tokens.**
- `AddServer()` registers the OpenIddict token server services in the DI container. Contains `OpenIddictServerBuilder` configurations.
- `AddCore()` registers the OpenIddict core services in the DI container. Contains `OpenIddictCoreBuilder` configurations.
- `AddValidation()` registers the OpenIddict token validation services in the DI container. Contains `OpenIddictValidationBuilder` configurations.
Domain also implements four store interfaces in OpenIddict, OpenIddict uses store to manage entities, corresponding to the above four entities, Custom entity repository is used in the store.
### OpenIddictCoreBuilder
`OpenIddictCoreBuilder` contains extension methods to configure the OpenIddict core services.
```cs
//Manager
OpenIddictApplicationManager
OpenIddictScopeManager
OpenIddictAuthorizationManager
OpenIddictTokenManager
//Store
IOpenIddictApplicationStore
IOpenIddictScopeStore
IOpenIddictAuthorizationStore
IOpenIddictTokenStore
//Repository
IOpenIddictApplicationRepository
IOpenIddictScopeRepository
IOpenIddictAuthorizationRepository
IOpenIddictTokenRepository
Example:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictCoreBuilder>(builder =>
{
//Set options here...
});
}
```
We enabled most of OpenIddict's features in the `AddOpenIddict` method, You can change OpenIddict's related builder options via `PreConfigure`.
These services contain:
```cs
PreConfigure<OpenIddictBuilder>(builder =>
{
//builder
});
- Adding `ApplicationStore`, `AuthorizationStore`, `ScopeStore`, `TokenStore`.
- Replacing `ApplicationManager`, `AuthorizationManager`, `ScopeManager`, `TokenManager`.
- Replacing `ApplicationStoreResolver`, `AuthorizationStoreResolver`, `ScopeStoreResolver`, `TokenStoreResolver`.
- Setting `DefaultApplicationEntity`, `DefaultAuthorizationEntity`, `DefaultScopeEntity`, `DefaultTokenEntity`.
### OpenIddictServerBuilder
PreConfigure<OpenIddictCoreBuilder>(builder =>
`OpenIddictServerBuilder` contains extension methods to configure OpenIddict server services.
Example:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//builder
});
PreConfigure<OpenIddictServerBuilder>(builder =>
{
//Set options here...
});
}
```
These services contain:
- Registering claims, scopes.
- Setting the `Issuer` URI that is used as the base address for the endpoint URIs returned from the discovery endpoint.
- Adding development signing keys, encryption/signing keys, credentials, and certificates.
- Adding/removing event handlers.
- Enabling/disabling grant types.
- Setting authentication server endpoint URIs.
### OpenIddictValidationBuilder
PreConfigure<OpenIddictServerBuilder>(builder =>
`OpenIddictValidationBuilder` contains extension methods to configure OpenIddict validation services.
Example:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//builder
});
PreConfigure<OpenIddictValidationBuilder>(builder =>
{
//Set options here...
});
}
```
#### AbpOpenIddictAspNetCoreOptions
These services contain:
- `AddAudiances()` for resource servers.
- `SetIssuer()` URI that is used to determine the actual location of the OAuth 2.0/OpenID Connect configuration document when using provider discovery.
- `SetConfiguration()` to configure `OpenIdConnectConfiguration`.
- `UseIntrospection()` to use introspection instead of local/direct validation.
- Adding encryption key, credentials, and certificates.
- Adding/removing event handlers.
- `SetClientId() ` to set the client identifier `client_id ` when communicating with the remote authorization server (e.g for introspection).
- `SetClientSecret()` to set the identifier `client_secret` when communicating with the remote authorization server (e.g for introspection).
- `EnableAuthorizationEntryValidation()` to enable authorization validation to ensure the `access token` is still valid by making a database call for each API request. *Note:* This may have a negative impact on performance and can only be used with an OpenIddict-based authorization server.
- `EnableTokenEntryValidation()` to enable authorization validation to ensure the `access token` is still valid by making a database call for each API request. *Note:* This may have a negative impact on performance and it is required when the OpenIddict server is configured to use reference tokens.
- `UseLocalServer()` to register the OpenIddict validation/server integration services.
- `UseAspNetCore()` to register the OpenIddict validation services for ASP.NET Core in the DI container.
## Internals
### Domain Layer
#### Aggregates
##### OpenIddictApplication
OpenIddictApplications represent the applications that can request tokens from your OpenIddict Server.
- `OpenIddictApplications` (aggregate root): Represents an OpenIddict application.
- `ClientId` (string): The client identifier associated with the current application.
- `ClientSecret` (string): The client secret associated with the current application. Maybe hashed or encrypted for security reasons.
- `ConsentType` (string): The consent type associated with the current application.
- `DisplayName` (string): The display name associated with the current application.
- `DisplayNames` (string): The localized display names associated with the current application serialized as a JSON object.
- `Permissions` (string): The permissions associated with the current application, serialized as a JSON array.
- `PostLogoutRedirectUris` (string): The logout callback URLs associated with the current application, serialized as a JSON array.
- `Properties` (string): The additional properties associated with the current application serialized as a JSON object or null.
- `RedirectUris` (string): The callback URLs associated with the current application, serialized as a JSON array.
- `Requirements` (string): The requirements associated with the current application
- `Type` (string): The application type associated with the current application.
- `ClientUri` (string): URI to further information about client.
- `LogoUri` (string): URI to client logo.
##### OpenIddictAuthorization
OpenIddictAuthorizations are used to keep the allowed scopes, authorization flow types.
- `OpenIddictAuthorization` (aggregate root): Represents an OpenIddict authorization.
- `ApplicationId` (Guid?): The application associated with the current authorization.
- `Properties` (string): The additional properties associated with the current authorization serialized as a JSON object or null.
- `Scopes` (string): The scopes associated with the current authorization, serialized as a JSON array.
- `Status` (string): The status of the current authorization.
- `Subject` (string): The subject associated with the current authorization.
- `Type` (string): The type of the current authorization.
##### OpenIddictScope
OpenIddictScopes are used to keep the scopes of resources.
- `OpenIddictScope` (aggregate root): Represents an OpenIddict scope.
- `Description` (string): The public description associated with the current scope.
- `Descriptions` (string): The localized public descriptions associated with the current scope, serialized as a JSON object.
- `DisplayName` (string): The display name associated with the current scope.
- `DisplayNames` (string): The localized display names associated with the current scope serialized as a JSON object.
- `Name` (string): The unique name associated with the current scope.
- `Properties` (string): The additional properties associated with the current scope serialized as a JSON object or null.
- `Resources` (string): The resources associated with the current scope, serialized as a JSON array.
##### OpenIddictToken
OpenIddictTokens are used to persist the application tokens.
- `OpenIddictToken` (aggregate root): Represents an OpenIddict token.
- `ApplicationId` (Guid?): The application associated with the current token.
- `AuthorizationId` (Guid?): The application associated with the current token.
- `CreationDate` (DateTime?): The UTC creation date of the current token.
- `ExpirationDate` (DateTime?): The UTC expiration date of the current token.
- `Payload` (string): The payload of the current token, if applicable. Only used for reference tokens and may be encrypted for security reasons.
- `Properties` (string): The additional properties associated with the current token serialized as a JSON object or null.
- `RedemptionDate` (DateTime?): The UTC redemption date of the current token.
- `Status` (string): The status of the current authorization.
- `ReferenceId` (string): The reference identifier associated with the current token, if applicable. Only used for reference tokens and may be hashed or encrypted for security reasons.
- `Status` (string): The status of the current token.
- `Subject` (string): The subject associated with the current token.
- `Type` (string): The type of the current token.
`UpdateAbpClaimTypes(default: true)`: Updates AbpClaimTypes to be compatible with identity server claims.
`AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
#### Stores
You can also change this options via `PreConfigure`.
This module implements OpenIddict stores:
#### Automatically removing orphaned tokens/authorizations
- `IAbpOpenIdApplicationStore`
- `IOpenIddictAuthorizationStore`
- `IOpenIddictScopeStore`
- `IOpenIddictTokenStore`
There is a background task in the `Domain` module (`enabled by default`) that automatically removes orphaned tokens/authorizations, you can configure `TokenCleanupOptions` to manage it.
##### Repositories
### ASP NET Core module
The following custom repositories are defined in this module:
- `IOpenIddictApplicationRepository`
- `IOpenIddictAuthorizationRepository`
- `IOpenIddictScopeRepository`
- `IOpenIddictTokenRepository`
##### Domain Services
This module doesn't contain any domain service but overrides the service below:
- `AbpApplicationManager` used to populate/get `AbpApplicationDescriptor` information that contains `ClientUri` and `LogoUri`.
### Database Providers
#### Common
##### Table/Collection Prefix & Schema
All tables/collections use the `OpenIddict` prefix by default. Set static properties on the `AbpOpenIddictDbProperties` class if you need to change the table prefix or set a schema name (if supported by your database provider).
##### Connection String
This module uses `AbpOpenIddict` for the connection string name. If you don't define a connection string with this name, it fallbacks to the `Default` connection string.
See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details.
#### Entity Framework Core
##### Tables
- **OpenIddictApplications**
- **OpenIddictAuthorizations**
- **OpenIddictScopes**
- **OpenIddictTokens**
#### MongoDB
##### Collections
- **OpenIddictApplications**
- **OpenIddictAuthorizations**
- **OpenIddictScopes**
- **OpenIddictTokens**
## ASP.NET Core Module
This module integrates ASP NET Core, with built-in MVC controllers for four protocols. It uses OpenIddict's [Pass-through mode](https://documentation.openiddict.com/guides/index.html#pass-through-mode).
@ -107,22 +273,76 @@ LogoutController -> connect/logout
UserInfoController -> connect/userinfo
```
> We will implement the related functions of **device flow** in the PRO module..
> **Device flow** implementation will be done in the commercial module.
#### AbpOpenIddictAspNetCoreOptions
`AbpOpenIddictAspNetCoreOptions` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
Example:
```csharp
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
//Set options here...
});
```
`AbpOpenIddictAspNetCoreOptions` properties:
- `UpdateAbpClaimTypes(default: true)`: Updates `AbpClaimTypes` to be compatible with the Openiddict claims.
- `AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
#### Automatically Removing Orphaned Tokens/Authorizations
The background task that automatically removes orphaned tokens/authorizations. This can be configured by `TokenCleanupOptions` to manage it.
`TokenCleanupOptions` can be configured in the `PreConfigureServices` method of your OpenIddict [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics).
Example:
```csharp
PreConfigure<TokenCleanupOptions>(options =>
{
//Set options here...
});
```
#### How to control claims in access_token and id_token
`TokenCleanupOptions` properties:
You can use the [Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) to add/remove claims to the `ClaimsPrincipal`.
- `IsCleanupEnabled` (default: true): Enable/disable token clean up.
- `CleanupPeriod` (default: 3,600,000 ms): Setting clean up period.
- `DisableAuthorizationPruning`: Setting a boolean indicating whether authorizations pruning should be disabled.
- `DisableTokenPruning`: Setting a boolean indicating whether token pruning should be disabled.
- `MinimumAuthorizationLifespan` (default: 14 days): Setting the minimum lifespan authorizations must have to be pruned. Cannot be less than 10 minutes.
- `MinimumTokenLifespan` (default: 14 days): Setting the minimum lifespan tokens must have to be pruned. Cannot be less than 10 minutes.
The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`.
#### Updating Claims In Access_token and Id_token
You can create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims
[Claims Principal Factory](https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory) can be used to add/remove claims to the `ClaimsPrincipal`.
The `AbpDefaultOpenIddictClaimDestinationsProvider` service will add `Name`, `Email,` and `Role` types of Claims to `access_token` and `id_token`, other claims are only added to `access_token` by default, and remove the `SecurityStampClaimType` secret claim of `Identity`.
Create a service that inherits from `IAbpOpenIddictClaimDestinationsProvider` and add it to DI to fully control the destinations of claims.
```cs
public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvider, ITransientDependency
{
public virtual Task SetDestinationsAsync(AbpOpenIddictClaimDestinationsProviderContext context)
{
// ...
foreach (var claim in context.Claims)
{
if (claim.Type == MyClaims.MyClaimsType)
{
claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken);
}
if (claim.Type == MyClaims.MyClaimsType2)
{
claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
}
}
return Task.CompletedTask;
}
}
@ -133,30 +353,11 @@ Configure<AbpOpenIddictClaimDestinationsOptions>(options =>
});
```
For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
### EF Core module
Implements the above four repository interfaces.
### MongoDB module
Implements the above four repository interfaces.
## OpenIddict
For detailed information, please refer to: [OpenIddict claim destinations](https://documentation.openiddict.com/configuration/claim-destinations.html)
### Documentation
#### Disable AccessToken Encryption
For more details about OpenIddict, please refer to its official documentation and Github.
https://documentation.openiddict.com
https://github.com/openiddict/openiddict-core#resources
### Disable AccessToken Encryption
ABP disables the `access token encryption` by default for compatibility, you can manually enable it if needed.
ABP disables the `access token encryption` by default for compatibility, it can be enabled manually if needed.
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
@ -170,22 +371,15 @@ public override void PreConfigureServices(ServiceConfigurationContext context)
https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption
### PKCE
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
### Request/Response process
I will briefly introduce the principle of OpenIddict so that everyone can quickly understand it.
### Request/Response Process
The `OpenIddict.Server.AspNetCore` adds an authentication scheme(`Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler`) and implements the `IAuthenticationRequestHandler` interface.
It will be executed first in `AuthenticationMiddleware` and can short-circuit the current request. Otherwise, `DefaultAuthenticateScheme` will be called and continue to execute the pipeline.
`OpenIddictServerAspNetCoreHandler` will call various built-in handlers(Handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
`OpenIddictServerAspNetCoreHandler` will call various built-in handlers (handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
Example a token request:
Example of a token request:
```
POST /connect/token HTTP/1.1
@ -199,11 +393,11 @@ Content-Type: application/x-www-form-urlencoded
scope=AbpAPI offline_access
```
This request will be processed by various handlers. They will confirm the endpoint type of the request, check `http/https`, verify that the request parameters (`client. scope etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
This request will be processed by various handlers. They will confirm the endpoint type of the request, check `HTTP/HTTPS`, verify that the request parameters (`client. scope, etc`) are valid and exist in the database, etc. Various protocol checks. And build a `OpenIddictRequest` object, If there are any errors, the response content may be set and directly short-circuit the current request.
If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the http request at this time. The rest of our work will be based on this object.
If everything is ok, the request will go to our processing controller(eg `TokenController`), we can get an `OpenIddictRequest` from the HTTP request at this time. The rest will be based on this object.
We may check the `username` and `password` in the request. If it is correct we create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
Check the `username` and `password` in the request. If it is correct create a `ClaimsPrincipal` object and return a `SignInResult`, which uses the `OpenIddict.Validation.AspNetCore` authentication scheme name, will calls `OpenIddictServerAspNetCoreHandler` for processing.
`OpenIddictServerAspNetCoreHandler` do some checks to generate json and replace the http response content.
@ -214,10 +408,26 @@ If you need to customize OpenIddict, you need to replace/delete/add new handlers
Please refer to:
https://documentation.openiddict.com/guides/index.html#events-model
## Migrating Guide
### PKCE
[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
## Sponsor
## Demo projects
In the module's `app` directory there are six projects(including `angular`)
* `OpenIddict.Demo.Server`: An abp application with integrated modules (has two `clients` and a `scope`).
* `OpenIddict.Demo.API`: ASP NET Core API application using JwtBearer authentication.
* `OpenIddict.Demo.Client.Mvc`: ASP NET Core MVC application using `OpenIdConnect` for authentication.
* `OpenIddict.Demo.Client.Console`: Use `IdentityModel` to test OpenIddict's various endpoints, and call the api of `OpenIddict.Demo.API`.
* `OpenIddict.Demo.Client.BlazorWASM:` ASP NET Core Blazor application using `OidcAuthentication` for authentication.
* `angular`: An angular application that integrates the abp ng modules and uses oauth for authentication.
Please consider sponsoring this project: https://github.com/sponsors/kevinchalet
#### How to run?
Confirm the connection string of `appsettings.json` in the `OpenIddict.Demo.Server` project. Running the project will automatically create the database and initialize the data.
After running the `OpenIddict.Demo.API` project, then you can run the rest of the projects to test.
## Migrating Guide
[Migrating from IdentityServer to OpenIddict Step by Step Guide ](../Migration-Guides/OpenIddict-Step-by-Step.md)

@ -78,7 +78,7 @@ Based on the options you've specified, you will get a slightly different solutio
If you don't specify any additional options, you will have a solution as shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png)
![bookstore-rider-solution-v6](../images/solution-structure-solution-explorer-rider.png)
Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
@ -225,7 +225,7 @@ So, the resulting solution allows a 4-tiered deployment, by comparing to 3-tiere
The solution structure is shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-tiered.png)
![bookstore-rider-solution-v6](../images/bookstore-rider-solution-tiered.png)
As different from the default structure, two new projects come into play: `.AuthServer` & `.HttpApi.Host`.
@ -233,9 +233,9 @@ As different from the default structure, two new projects come into play: `.Auth
This project is used as an authentication server for other projects. `.Web` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the AuthServer. Then uses the access token to call the HTTP API server. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
![tiered-solution-applications](../images/tiered-solution-applications.png)
![tiered-solution-applications](../images/tiered-solution-applications-authserver.png)
ABP uses the open source [OpenIddcit](https://github.com/openiddict/openiddict-core) framework for the authentication between applications. See [OpenIddcit documentation](https://documentation.openiddict.com/) for details about the OpenIddict and OpenID Connect protocol.
ABP uses the [OpenIddict Module](../Modules/OpenIddict.md) that uses the open-source [OpenIddict-core](https://github.com/openiddict/openiddict-core) library for the authentication between applications. See [OpenIddict documentation](https://documentation.openiddict.com/) for details about the OpenIddict and OpenID Connect protocol.
It has its own `appsettings.json` that contains database connection and other configurations.

@ -0,0 +1,20 @@
# Angular UI: Current User
The current user information stored in Config State.
### How to Get a Current User Information Configuration
You can use the `getOne` or `getOne$` method of `ConfigStateService` to get a specific configuration property. For that, the property name should be passed to the method as parameter.
```js
// this.config is an instance of ConfigStateService
const currentUser = this.config.getOne("currentUser");
// or
this.config.getOne$("currentUser").subscribe(currentUser => {
// use currentUser here
})
```
> See the [ConfigStateService](./Config-State-Service) for more information.

@ -981,6 +981,10 @@
"text": "Authorization",
"path": "UI/Angular/Authorization.md"
},
{
"text": "Current User",
"path": "UI/Angular/Current-User.md"
},
{
"text": "HTTP Requests",
"path": "UI/Angular/HTTP-Requests.md"
@ -1282,10 +1286,6 @@
{
"text": "Deploying to a Clustered Environment",
"path": "Deployment/Clustered-Environment.md"
},
{
"text": "Deploy Abp Webapp to Azure App Service",
"path": "Deployment/Deploy-Azure-App-Service.md"
}
]
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

@ -1,3 +1,182 @@
## Validation
# Validação
Façam
O sistema de validação é utilizado para validar a entrada do usuário ou a requisição do cliente para uma ação de um controller ou por um serviço.
O ABP é compatível com o sistema de Validação de Modelos do ASP.NET Core e tudo escrito na [sua documentação](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) é válido para aplicações baseadas no ABP. Logo, esse documento foca nas funcionalidades do ABP ao invés de repetir a documentação da Microsoft.
Além disso, o ABP adiciona os seguintes benefícios:
* Define `IValidationEnabled` para adicionar validação automática para uma classe qualquer. Como todos os [serviços de aplicação](Application-Services.md) já o implementam, eles também são validados automaticamente.
* Automaticamente traduz os erros de validação para os atributos de anotação de dados.
* Provê serviços extensíveis para validar a chamada de um método ou o estado de um objeto.
* Provê integração com o [FluentValidation](https://fluentvalidation.net/)
## Validando DTOs
Essa seção introduz brevemente o sistema de validação. Para mais detalhes, veja a [Documentação da Validação de Modelo em ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation).
### Atributos de anotação de dados
Utilizar anotações de dados é uma maneira simples de implementar uma validação formal para um [DTO](Data-Transfer-Objects.md) de uma forma declarativa. Exemplo:
````csharp
public class CreateBookDto
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
}
````
Quando você utilizar essa classe como parâmetro para um [serviço da aplicação](Application-Services.md) ou um controller, ele será automaticamente validado e a validação traduzida será lançada ([e tratada](Exception-Handling.md) pelo ABP framework).
### IValidatableObject
`IValidatableObject` pode ser implementado por um DTO para executar uma lógica customizada de validação. O `CreateBookDto` no exemplo a seguir implementa essa interface e verifica se o `Name` é igual a `Description` e retorna um erro de validação nesse caso.
````csharp
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Acme.BookStore
{
public class CreateBookDto : IValidatableObject
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(1000)]
public string Description { get; set; }
[Range(0, 999.99)]
public decimal Price { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (Name == Description)
{
yield return new ValidationResult(
"Name and Description can not be the same!",
new[] { "Name", "Description" }
);
}
}
}
}
````
#### Resolvendo um serviço.
Se você precisar resolver um serviço do [sistema de injeção de dependências](Dependency-Injection.md), você pode utilizar o objeto `ValidationContext`.
````csharp
var myService = validationContext.GetRequiredService<IMyService>();
````
> Enquanto resolver os serviços no método `Validate` permite várias possibilidades, não é um boa prática implementar sua lógica de validação do domínio nos DTOs. Mantenha os DTOs simples. Seu propósito é transferir dados (DTO: Data Transfer Object, ou Objeto de Transferência de Dados).
## Infraestrutura de Validação.
Essa seção explica alguns serviços adicionais fornecidos pelo ABP Framework.
### Interface IValidationEnabled
`IValidationEnabled` é um marcador vazio de interface que pode ser implementado por qualquer classe (registrada e resolvida a partir do [DI](Dependency-Injection.md)) para permitir que o ABP framework realize o sistema de validação para os métodos da classe. Por exemplo:
````csharp
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation;
namespace Acme.BookStore
{
public class MyService : ITransientDependency, IValidationEnabled
{
public virtual async Task DoItAsync(MyInput input)
{
//...
}
}
}
````
> O ABP framework utiliza o sistema de [Proxying Dinâmico / Interceptadores](Dynamic-Proxying-Interceptors.md) para realizar a validação. Para fazê-lo funcionar, seu método deve ser **virtual** ou seu serviço deve ser injetado e utilizado através de uma **interface** (como `IMyService`).
#### Habilitando e Desabilitando Validações
Você pode utilizar o `[DisableValidation]` e desabilitar a validação para métodos, classes e propriedades.
````csharp
[DisableValidation]
public Void MyMethod()
{
}
[DisableValidation]
public class InputClass
{
public string MyProperty { get; set; }
}
public class InputClass
{
[DisableValidation]
public string MyProperty { get; set; }
}
````
### AbpValidationException
Uma vez que o ABP determina um erro de validação, é lançada uma validação do tipo `AbpValidationException`. O código da sua aplicação poderá lançar o `AbpValidationException`, mas na maioria das vezes não será necessário.
* A propriedade `ValidationErrors` do `AbpValidationException` contem a lista com os erros de validação.
* O nível de log do `AbpValidationException` é definido como `Warning`. Todos os erros de validação são logados no [Sistema de Logging](Logging.md).
* `AbpValidationException` é tratado automaticamente pelo ABP framework e é convertido em um erro utilizável com o código de status HTTP 400. Veja a documentação de [Manipulação de Exceção](Exception-Handling.md) para mais informações.
## Tópicos Avançados
### IObjectValidator
Além da validação automática, você pode querer validar um objeto manualmente. Nesse caso, [injete](Dependency-Injection.md) e use o serviço `IObjectValidator`:
* O método `ValidateAsync` valida o objeto informado baseado nas regras de validação e lança uma `AbpValidationException` se não estiver em um estado válido.
* `GetErrorsAsync` não lança uma exceção, somente retorna os erros de validação.
`IObjectValidator` é implementado pelo `ObjectValidator` por padrão. `ObjectValidator` é extensível; você pode implementar a interface `IObjectValidationContributor` para contribuir com uma lógica customizada. Exemplo:
````csharp
public class MyObjectValidationContributor
: IObjectValidationContributor, ITransientDependency
{
public Task AddErrorsAsync(ObjectValidationContext context)
{
//Get the validating object
var obj = context.ValidatingObject;
//Add the validation errors if available
context.Errors.Add(...);
return Task.CompletedTask;
}
}
````
* Lembre-se de registrar sua classe no [DI](Dependency-Injection.md) (implementar `ITransientDependency` faz isso no exemplo anterior)
* ABP vai automaticamente descobrir sua classe e utilizá-la em qualquer tipo de validação de objetos (incluindo chamadas de métodos de validação automáticas).
### IMethodInvocationValidator
`IMethodInvocationValidator` é utilizado para validar a chamada de um método. Ele utiliza internamente o `IObjectValidator` para validar os objetos passados na chamada do método. Você normalmente não precisa deste serviço, já que ele é utilizado automaticamente pelo framework, mas você pode querer reutilizar ou substituir na sua aplicação em alguns casos raros.
## Integração com FluentValidation
O pacote Volo.Abp.FluentValidation integra a biblioteca FluentValidation com o sistema de validação (implementando o `IObjectValidationContributor`). Veja o [documento de Integração com o FluentValidation](FluentValidation.md) para mais informações.

@ -409,6 +409,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.RemoteServices", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.PlugIn", "test\Volo.Abp.AspNetCore.Mvc.PlugIn\Volo.Abp.AspNetCore.Mvc.PlugIn.csproj", "{C6D6D878-208A-4FD2-822E-365545D8681B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Dapr", "src\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj", "{192A829F-D608-4E41-8DE0-058E943E453F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Dapr", "src\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj", "{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Dapr", "src\Volo.Abp.Http.Client.Dapr\Volo.Abp.Http.Client.Dapr.csproj", "{18B796D2-D45D-41AE-9A42-75C9B14B20DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr", "src\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj", "{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Dapr.EventBus", "src\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus\Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.csproj", "{B02EF042-C39E-45C4-A92D-BF7554E1889D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking.Dapr", "src\Volo.Abp.DistributedLocking.Dapr\Volo.Abp.DistributedLocking.Dapr.csproj", "{CAE48068-233C-47A9-BEAB-DDF521730E7A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1219,6 +1231,30 @@ Global
{C6D6D878-208A-4FD2-822E-365545D8681B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6D6D878-208A-4FD2-822E-365545D8681B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6D6D878-208A-4FD2-822E-365545D8681B}.Release|Any CPU.Build.0 = Release|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{192A829F-D608-4E41-8DE0-058E943E453F}.Release|Any CPU.Build.0 = Release|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431}.Release|Any CPU.Build.0 = Release|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18B796D2-D45D-41AE-9A42-75C9B14B20DF}.Release|Any CPU.Build.0 = Release|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1}.Release|Any CPU.Build.0 = Release|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B02EF042-C39E-45C4-A92D-BF7554E1889D}.Release|Any CPU.Build.0 = Release|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAE48068-233C-47A9-BEAB-DDF521730E7A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1425,6 +1461,12 @@ Global
{3683340D-92F5-4B14-B77B-34A163333309} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{EDFFDA74-090D-439C-A58D-06CCF86D4423} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{C6D6D878-208A-4FD2-822E-365545D8681B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{192A829F-D608-4E41-8DE0-058E943E453F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{DCC41E99-EBC7-4F19-BA0D-A6F770D8E431} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{18B796D2-D45D-41AE-9A42-75C9B14B20DF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{5EED625D-8D86-492A-BCB8-F6C8CD8D4AA1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{B02EF042-C39E-45C4-A92D-BF7554E1889D} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{CAE48068-233C-47A9-BEAB-DDF521730E7A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.Dapr\Volo.Abp.AspNetCore.Mvc.Dapr.csproj" />
<ProjectReference Include="..\Volo.Abp.EventBus.Dapr\Volo.Abp.EventBus.Dapr.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http.Json;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
[DependsOn(
typeof(AbpAspNetCoreMvcDaprModule),
typeof(AbpEventBusDaprModule)
)]
public class AbpAspNetCoreMvcDaprEventBusModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// TODO: Add NewtonsoftJson json converter.
Configure<JsonOptions>(options =>
{
options.SerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
});
Configure<AbpSystemTextJsonSerializerOptions>(options =>
{
options.JsonSerializerOptions.Converters.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter());
});
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprEventBusOptions
{
public List<IAbpAspNetCoreMvcDaprPubSubProviderContributor> Contributors { get; }
public AbpAspNetCoreMvcDaprEventBusOptions()
{
Contributors = new List<IAbpAspNetCoreMvcDaprPubSubProviderContributor>();
}
}

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubConsts
{
public const string DaprSubscribeUrl = "dapr/subscribe";
public const string DaprEventCallbackUrl = "api/abp/dapr/event";
}

@ -0,0 +1,63 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Dapr;
using Volo.Abp.EventBus.Distributed;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubProvider : ITransientDependency
{
protected IServiceProvider ServiceProvider { get; }
protected AbpAspNetCoreMvcDaprEventBusOptions AspNetCoreMvcDaprEventBusOptions { get; }
protected AbpDaprEventBusOptions DaprEventBusOptions { get; }
protected AbpDistributedEventBusOptions DistributedEventBusOptions { get; }
public AbpAspNetCoreMvcDaprPubSubProvider(
IServiceProvider serviceProvider,
IOptions<AbpAspNetCoreMvcDaprEventBusOptions> aspNetCoreDaprEventBusOptions,
IOptions<AbpDaprEventBusOptions> daprEventBusOptions,
IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions)
{
ServiceProvider = serviceProvider;
AspNetCoreMvcDaprEventBusOptions = aspNetCoreDaprEventBusOptions.Value;
DaprEventBusOptions = daprEventBusOptions.Value;
DistributedEventBusOptions = distributedEventBusOptions.Value;
}
public virtual async Task<List<AbpAspNetCoreMvcDaprSubscriptionDefinition>> GetSubscriptionsAsync()
{
var subscriptions = new List<AbpAspNetCoreMvcDaprSubscriptionDefinition>();
foreach (var handler in DistributedEventBusOptions.Handlers)
{
foreach (var @interface in handler.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDistributedEventHandler<>)))
{
var eventType = @interface.GetGenericArguments()[0];
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
subscriptions.Add(new AbpAspNetCoreMvcDaprSubscriptionDefinition()
{
PubSubName = DaprEventBusOptions.PubSubName,
Topic = eventName,
Route = AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl
});
}
}
if (AspNetCoreMvcDaprEventBusOptions.Contributors.Any())
{
using (var scope = ServiceProvider.CreateScope())
{
var context = new AbpAspNetCoreMvcDaprPubSubProviderContributorContext(scope.ServiceProvider, subscriptions);
foreach (var contributor in AspNetCoreMvcDaprEventBusOptions.Contributors)
{
await contributor.ContributeAsync(context);
}
}
}
return subscriptions;
}
}

@ -0,0 +1,16 @@
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public class AbpAspNetCoreMvcDaprPubSubProviderContributorContext
{
public IServiceProvider ServiceProvider { get; }
public List<AbpAspNetCoreMvcDaprSubscriptionDefinition> Subscriptions { get; }
public AbpAspNetCoreMvcDaprPubSubProviderContributorContext(IServiceProvider serviceProvider, List<AbpAspNetCoreMvcDaprSubscriptionDefinition> daprSubscriptionModels)
{
ServiceProvider = serviceProvider;
Subscriptions = daprSubscriptionModels;
}
}

@ -0,0 +1,36 @@
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
using Volo.Abp.Dapr;
using Volo.Abp.EventBus.Dapr;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Controllers;
[Area("abp")]
[RemoteService(Name = "abp")]
public class AbpAspNetCoreMvcDaprPubSubController : AbpController
{
[HttpGet(AbpAspNetCoreMvcDaprPubSubConsts.DaprSubscribeUrl)]
public virtual async Task<List<AbpAspNetCoreMvcDaprSubscriptionDefinition>> SubscribeAsync()
{
return await HttpContext.RequestServices.GetRequiredService<AbpAspNetCoreMvcDaprPubSubProvider>().GetSubscriptionsAsync();
}
[HttpPost(AbpAspNetCoreMvcDaprPubSubConsts.DaprEventCallbackUrl)]
public virtual async Task<IActionResult> EventsAsync()
{
var bodyJsonDocument = await JsonDocument.ParseAsync(HttpContext.Request.Body);
var request = JsonSerializer.Deserialize<AbpAspNetCoreMvcDaprSubscriptionRequest>(bodyJsonDocument.RootElement.GetRawText(),
HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions);
var distributedEventBus = HttpContext.RequestServices.GetRequiredService<DaprDistributedEventBus>();
var daprSerializer = HttpContext.RequestServices.GetRequiredService<IDaprSerializer>();
var eventData = daprSerializer.Deserialize(bodyJsonDocument.RootElement.GetProperty("data").GetRawText(), distributedEventBus.GetEventType(request.Topic));
await distributedEventBus.TriggerHandlersAsync(distributedEventBus.GetEventType(request.Topic), eventData);
return Ok();
}
}

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus;
public interface IAbpAspNetCoreMvcDaprPubSubProviderContributor
{
Task ContributeAsync(AbpAspNetCoreMvcDaprPubSubProviderContributorContext context);
}

@ -0,0 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionDefinition
{
public string PubSubName { get; set; }
public string Topic { get; set; }
public string Route { get; set; }
}

@ -0,0 +1,8 @@
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
public class AbpAspNetCoreMvcDaprSubscriptionRequest
{
public string PubSubName { get; set; }
public string Topic { get; set; }
}

@ -0,0 +1,11 @@
using System.Text.Json;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
public class AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return name.ToLower();
}
}

@ -0,0 +1,25 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.Models;
namespace Volo.Abp.AspNetCore.Mvc.Dapr.EventBus.SystemTextJson;
public class AbpAspNetCoreMvcDaprSubscriptionDefinitionConverter : JsonConverter<AbpAspNetCoreMvcDaprSubscriptionDefinition>
{
private JsonSerializerOptions _writeJsonSerializerOptions;
public override AbpAspNetCoreMvcDaprSubscriptionDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, AbpAspNetCoreMvcDaprSubscriptionDefinition value, JsonSerializerOptions options)
{
_writeJsonSerializerOptions ??= JsonSerializerOptionsHelper.Create(new JsonSerializerOptions(options)
{
PropertyNamingPolicy = new AbpAspNetCoreMvcDaprPubSubJsonNamingPolicy()
}, x => x == this);
JsonSerializer.Serialize(writer, value, _writeJsonSerializerOptions);
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapr.AspNetCore" Version="1.8.0" />
</ItemGroup>
</Project>

@ -0,0 +1,13 @@
using Volo.Abp.Dapr;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.Dapr;
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpDaprModule)
)]
public class AbpAspNetCoreMvcDaprModule : AbpModule
{
}

@ -5,5 +5,11 @@ public enum AbpModalSize
Default,
Small,
Large,
ExtraLarge
ExtraLarge,
Fullscreen,
FullscreenSmDown,
FullscreenMdDown,
FullscreenLgDown,
FullscreenXlDown,
FullscreenXxlDown
}

@ -14,6 +14,18 @@ public static class AbpModalSizeExtensions
return "modal-xl";
case AbpModalSize.Default:
return "";
case AbpModalSize.Fullscreen:
return "modal-fullscreen";
case AbpModalSize.FullscreenSmDown:
return "modal-fullscreen-sm-down";
case AbpModalSize.FullscreenMdDown:
return "modal-fullscreen-md-down";
case AbpModalSize.FullscreenLgDown:
return "modal-fullscreen-lg-down";
case AbpModalSize.FullscreenXlDown:
return "modal-fullscreen-xl-down";
case AbpModalSize.FullscreenXxlDown:
return "modal-fullscreen-xxl-down";
default:
throw new AbpException($"Unknown {nameof(AbpModalSize)}: {size}");
}

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Autofac.Core;
using Autofac.Extras.DynamicProxy;
using Volo.Abp.Autofac;
using Volo.Abp.Castle.DynamicProxy;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
@ -67,10 +68,11 @@ public static class AbpRegistrationBuilderExtensions
Type implementationType)
where TActivatorData : ReflectionActivatorData
{
//Enable Property Injection only for types in an assembly containing an AbpModule
if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly))
// Enable Property Injection only for types in an assembly containing an AbpModule and without a DisablePropertyInjection attribute on class or properties.
if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly) &&
implementationType.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty())
{
registrationBuilder = registrationBuilder.PropertiesAutowired();
registrationBuilder = registrationBuilder.PropertiesAutowired(new AbpPropertySelector(false));
}
return registrationBuilder;

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Reflection;
using Autofac.Core;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Autofac;
public class AbpPropertySelector : DefaultPropertySelector
{
public AbpPropertySelector(bool preserveSetValues)
: base(preserveSetValues)
{
}
public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
return propertyInfo.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty() &&
base.InjectProperty(propertyInfo, instance);
}
}

@ -74,9 +74,9 @@ public class AuthService : IAuthService, ITransientDependency
{
var configuration = new IdentityClientConfiguration(
CliUrls.AccountAbpIo,
"role email abpio abpio_www abpio_commercial offline_access",
"abpio offline_access",
"abp-cli",
"1q2w3e*",
null,
OidcConstants.GrantTypes.Password,
userName,
password
@ -96,9 +96,9 @@ public class AuthService : IAuthService, ITransientDependency
{
var configuration = new IdentityClientConfiguration(
CliUrls.AccountAbpIo,
"role email abpio abpio_www abpio_commercial openid offline_access",
"abpio offline_access",
"abp-cli",
"1q2w3e*",
null,
OidcConstants.GrantTypes.DeviceCode
);

@ -113,8 +113,11 @@ public class BundlingService : IBundlingService, ITransientDependency
var contributor = CreateContributorInstance(bundleDefinition.BundleContributorType);
contributor.AddScripts(scriptContext);
}
scriptContext.BundleDefinitions.AddIfNotContains(
x => x.Source == "_framework/blazor.webassembly.js",
() => new BundleDefinition { Source = "_framework/blazor.webassembly.js" });
scriptContext.Add("_framework/blazor.webassembly.js");
return scriptContext;
}
@ -206,7 +209,7 @@ public class BundlingService : IBundlingService, ITransientDependency
builder.Append($" <script src=\"{script.Source}\"");
foreach (var additionalProperty in script.AdditionalProperties)
{
builder.Append($"{additionalProperty.Key}={additionalProperty.Value} ");
builder.Append($" {additionalProperty.Key}={additionalProperty.Value} ");
}
builder.AppendLine("></script>");

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Bundling;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.ProjectBuilding;
@ -26,23 +27,35 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
protected TemplateProjectBuilder TemplateProjectBuilder { get; }
public ITemplateInfoProvider TemplateInfoProvider { get; }
public NewCommand(TemplateProjectBuilder templateProjectBuilder
, ITemplateInfoProvider templateInfoProvider,
ConnectionStringProvider connectionStringProvider,
public NewCommand(
ConnectionStringProvider connectionStringProvider,
SolutionPackageVersionFinder solutionPackageVersionFinder,
ICmdHelper cmdHelper,
IInstallLibsService installLibsService,
AngularPwaSupportAdder angularPwaSupportAdder,
IInstallLibsService installLibsService,
CliService cliService,
AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus)
: base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, installLibsService, angularPwaSupportAdder, initialMigrationCreator, themePackageAdder, eventBus)
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus,
IBundlingService bundlingService,
ITemplateInfoProvider templateInfoProvider,
TemplateProjectBuilder templateProjectBuilder) :
base(connectionStringProvider,
solutionPackageVersionFinder,
cmdHelper,
installLibsService,
cliService,
angularPwaSupportAdder,
initialMigrationCreator,
themePackageAdder,
eventBus,
bundlingService)
{
TemplateProjectBuilder = templateProjectBuilder;
TemplateInfoProvider = templateInfoProvider;
TemplateProjectBuilder = templateProjectBuilder;
}
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
var projectName = NamespaceHelper.NormalizeNamespace(commandLineArgs.Target);
@ -72,7 +85,7 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
Logger.LogInformation("Tiered: yes");
}
var projectArgs = GetProjectBuildArgs(commandLineArgs, template, projectName);
var projectArgs = await GetProjectBuildArgsAsync(commandLineArgs, template, projectName);
var result = await TemplateProjectBuilder.BuildAsync(
projectArgs
@ -92,6 +105,8 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
await RunInstallLibsForWebTemplateAsync(projectArgs);
}
await RunBundleForBlazorWasmTemplateAsync(projectArgs);
await ConfigurePwaSupportForAngular(projectArgs);
OpenRelatedWebPage(projectArgs, template, isTiered, commandLineArgs);
@ -155,5 +170,4 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien
{
return "Generate a new solution based on the ABP startup templates.";
}
}

@ -10,6 +10,7 @@ using NuGet.Versioning;
using NUglify.Helpers;
using Volo.Abp.Cli.ProjectModification;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Bundling;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.LIbs;
using Volo.Abp.Cli.ProjectBuilding;
@ -25,10 +26,12 @@ namespace Volo.Abp.Cli.Commands;
public abstract class ProjectCreationCommandBase
{
private readonly IBundlingService _bundlingService;
public ConnectionStringProvider ConnectionStringProvider { get; }
public SolutionPackageVersionFinder SolutionPackageVersionFinder { get; }
public ICmdHelper CmdHelper { get; }
public IInstallLibsService InstallLibsService { get; }
public CliService CliService { get; }
public AngularPwaSupportAdder AngularPwaSupportAdder { get; }
public InitialMigrationCreator InitialMigrationCreator { get; }
public ILocalEventBus EventBus { get; }
@ -41,15 +44,19 @@ public abstract class ProjectCreationCommandBase
SolutionPackageVersionFinder solutionPackageVersionFinder,
ICmdHelper cmdHelper,
IInstallLibsService installLibsService,
CliService cliService,
AngularPwaSupportAdder angularPwaSupportAdder,
InitialMigrationCreator initialMigrationCreator,
ThemePackageAdder themePackageAdder,
ILocalEventBus eventBus)
ILocalEventBus eventBus,
IBundlingService bundlingService)
{
_bundlingService = bundlingService;
ConnectionStringProvider = connectionStringProvider;
SolutionPackageVersionFinder = solutionPackageVersionFinder;
CmdHelper = cmdHelper;
InstallLibsService = installLibsService;
CliService = cliService;
AngularPwaSupportAdder = angularPwaSupportAdder;
InitialMigrationCreator = initialMigrationCreator;
EventBus = eventBus;
@ -58,7 +65,7 @@ public abstract class ProjectCreationCommandBase
Logger = NullLogger<NewCommand>.Instance;
}
protected ProjectBuildArgs GetProjectBuildArgs(CommandLineArgs commandLineArgs, string template, string projectName)
protected async Task<ProjectBuildArgs> GetProjectBuildArgsAsync(CommandLineArgs commandLineArgs, string template, string projectName)
{
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
@ -71,6 +78,15 @@ public abstract class ProjectCreationCommandBase
if (preview)
{
Logger.LogInformation("Preview: yes");
var cliVersion = await CliService.GetCurrentCliVersionAsync(typeof(CliService).Assembly);
if (!cliVersion.IsPrerelease)
{
throw new CliUsageException(
"You can only create a new preview solution with preview CLI version." +
" Update your ABP CLI to the preview version.");
}
}
var pwa = commandLineArgs.Options.ContainsKey(Options.ProgressiveWebApp.Short);
@ -392,6 +408,25 @@ public abstract class ProjectCreationCommandBase
}
}
protected async Task RunBundleForBlazorWasmTemplateAsync(ProjectBuildArgs projectArgs)
{
if (AppTemplateBase.IsAppTemplate(projectArgs.TemplateName) && projectArgs.UiFramework == UiFramework.Blazor)
{
Logger.LogInformation("Generating bundles for Blazor Wasm...");
await EventBus.PublishAsync(new ProjectCreationProgressEvent
{
Message = "Generating bundles for Blazor Wasm"
}, false);
var directory = Path.GetDirectoryName(
Directory.GetFiles(projectArgs.OutputFolder, "*.Blazor.csproj", SearchOption.AllDirectories).First()
);
await _bundlingService.BundleAsync(directory, true);
}
}
protected async Task CreateInitialMigrationsAsync(ProjectBuildArgs projectArgs)
{
if (projectArgs.DatabaseProvider == DatabaseProvider.MongoDb)

@ -14,6 +14,16 @@ internal static class CommercialPackages
public static bool IsCommercial(string packageId)
{
return Packages.Contains(packageId.ToLowerInvariant());
return Packages.Contains(packageId.ToLowerInvariant()) || IsLeptonXPackage(packageId);
}
private static bool IsLeptonXPackage(string packageId)
{
return !IsLeptonXLitePackage(packageId) && packageId.Contains("LeptonX");
}
private static bool IsLeptonXLitePackage(string packageId)
{
return packageId.Contains("LeptonXLite");
}
}

@ -11,7 +11,7 @@ public class CreateAppSettingsSecretsStep : ProjectBuildPipelineStep
public override void Execute(ProjectBuildContext context)
{
var appSettingsFiles = context.Files
.Where(x => x.Name.EndsWith(CliConsts.AppSettingsJsonFileName) && NotBlazorWasmProject(x.Name))
.Where(x => x.Name.EndsWith(CliConsts.AppSettingsJsonFileName) && NotBlazorWasmProject(x.Name) && NotInDockerFiles(x.Name))
.ToList();
if (!appSettingsFiles.Any())
@ -66,6 +66,11 @@ public class CreateAppSettingsSecretsStep : ProjectBuildPipelineStep
return !fileName.Contains("Blazor/wwwroot") && !fileName.Contains("Blazor.Host/wwwroot");
}
private static bool NotInDockerFiles(string fileName)
{
return !fileName.Contains("etc/docker/");
}
private static string ReplaceAppSettingsSecretsPlaceholder(string content)
{
var path = string.Empty;

@ -41,10 +41,7 @@ public abstract class AppNoLayersTemplateBase : AppTemplateBase
break;
}
if (context.BuildArgs.DatabaseManagementSystem == DatabaseManagementSystem.PostgreSQL)
{
context.Symbols.Add("dbms:PostgreSQL");
}
context.Symbols.Add($"dbms:{context.BuildArgs.DatabaseManagementSystem}");
switch (context.BuildArgs.UiFramework)
{

@ -37,6 +37,7 @@ public abstract class AppTemplateBase : TemplateInfo
ConfigureTieredArchitecture(context, steps);
ConfigurePublicWebSite(context, steps);
ConfigureTheme(context, steps);
ConfigureVersion(context, steps);
RemoveUnnecessaryPorts(context, steps);
RandomizeSslPorts(context, steps);
RandomizeStringEncryption(context, steps);
@ -109,10 +110,7 @@ public abstract class AppTemplateBase : TemplateInfo
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MongoDB.Tests", projectFolderPath: "/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests"));
}
if (context.BuildArgs.DatabaseManagementSystem == DatabaseManagementSystem.PostgreSQL)
{
context.Symbols.Add("dbms:PostgreSQL");
}
context.Symbols.Add($"dbms:{context.BuildArgs.DatabaseManagementSystem}");
}
protected void DeleteUnrelatedProjects(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
@ -492,6 +490,14 @@ public abstract class AppTemplateBase : TemplateInfo
steps.Add(new RemoveUnnecessaryPortsStep());
}
protected void ConfigureVersion(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.Version == null || SemanticVersion.Parse(context.BuildArgs.Version) >= SemanticVersion.Parse("6.0.0-rc.1"))
{
context.Symbols.Add("newer-than-6.0");
}
}
protected void RandomizeSslPorts(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("no-random-port"))

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.DependencyInjection;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class DisablePropertyInjectionAttribute : Attribute
{
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapr.Client" Version="1.8.0" />
</ItemGroup>
</Project>

@ -0,0 +1,48 @@
using System.Collections.Concurrent;
using System.Text.Json;
using Dapr.Client;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json.SystemTextJson;
namespace Volo.Abp.Dapr;
public class AbpDaprClientFactory : ITransientDependency
{
protected AbpDaprOptions Options { get; }
protected AbpSystemTextJsonSerializerOptions SystemTextJsonSerializerOptions { get; }
public AbpDaprClientFactory(
IOptions<AbpDaprOptions> options,
IOptions<AbpSystemTextJsonSerializerOptions> systemTextJsonSerializerOptions)
{
Options = options.Value;
SystemTextJsonSerializerOptions = systemTextJsonSerializerOptions.Value;
}
public virtual async Task<DaprClient> CreateAsync()
{
var builder = new DaprClientBuilder()
.UseJsonSerializationOptions(await CreateJsonSerializerOptions());
if (!Options.HttpEndpoint.IsNullOrWhiteSpace())
{
builder.UseHttpEndpoint(Options.HttpEndpoint);
}
if (!Options.GrpcEndpoint.IsNullOrWhiteSpace())
{
builder.UseGrpcEndpoint(Options.GrpcEndpoint);
}
return builder.Build();
}
private readonly static ConcurrentDictionary<string, JsonSerializerOptions> JsonSerializerOptionsCache = new ConcurrentDictionary<string, JsonSerializerOptions>();
protected virtual Task<JsonSerializerOptions> CreateJsonSerializerOptions()
{
return Task.FromResult(JsonSerializerOptionsCache.GetOrAdd(nameof(AbpDaprClientFactory),
_ => new JsonSerializerOptions(SystemTextJsonSerializerOptions.JsonSerializerOptions)));
}
}

@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
namespace Volo.Abp.Dapr;
[DependsOn(typeof(AbpJsonModule))]
public class AbpDaprModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpDaprOptions>(configuration.GetSection("Dapr"));
}
}

@ -0,0 +1,10 @@
namespace Volo.Abp.Dapr;
public class AbpDaprOptions
{
public string AppId { get; set; }
public string HttpEndpoint { get; set; }
public string GrpcEndpoint { get; set; }
}

@ -0,0 +1,16 @@
namespace Volo.Abp.Dapr;
public interface IDaprSerializer
{
byte[] Serialize(object obj);
object Deserialize(byte[] value, Type type);
T Deserialize<T>(byte[] value);
string SerializeToString(object obj);
object Deserialize(string value, Type type);
T Deserialize<T>(string value);
}

@ -0,0 +1,45 @@
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Volo.Abp.Dapr;
public class Utf8JsonDaprSerializer : IDaprSerializer, ITransientDependency
{
private readonly IJsonSerializer _jsonSerializer;
public Utf8JsonDaprSerializer(IJsonSerializer jsonSerializer)
{
_jsonSerializer = jsonSerializer;
}
public byte[] Serialize(object obj)
{
return Encoding.UTF8.GetBytes(_jsonSerializer.Serialize(obj));
}
public object Deserialize(byte[] value, Type type)
{
return _jsonSerializer.Deserialize(type, Encoding.UTF8.GetString(value));
}
public T Deserialize<T>(byte[] value)
{
return _jsonSerializer.Deserialize<T>(Encoding.UTF8.GetString(value));
}
public string SerializeToString(object obj)
{
return _jsonSerializer.Serialize(obj);
}
public object Deserialize(string value, Type type)
{
return _jsonSerializer.Deserialize(type, value);
}
public T Deserialize<T>(string value)
{
return _jsonSerializer.Deserialize<T>(value);
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
<ProjectReference Include="..\Volo.Abp.DistributedLocking.Abstractions\Volo.Abp.DistributedLocking.Abstractions.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,13 @@
namespace Volo.Abp.DistributedLocking.Dapr;
public class AbpDistributedLockDaprOptions
{
public string StoreName { get; set; }
public TimeSpan DefaultTimeout { get; set; }
public AbpDistributedLockDaprOptions()
{
DefaultTimeout = TimeSpan.FromSeconds(30);
}
}

@ -0,0 +1,11 @@
using Volo.Abp.Dapr;
using Volo.Abp.Modularity;
namespace Volo.Abp.DistributedLocking.Dapr;
[DependsOn(
typeof(AbpDistributedLockingAbstractionsModule),
typeof(AbpDaprModule))]
public class AbpDistributedLockingDaprModule : AbpModule
{
}

@ -0,0 +1,50 @@
using Microsoft.Extensions.Options;
using Volo.Abp.Dapr;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.DistributedLocking.Dapr;
[Dependency(ReplaceServices = true)]
public class DaprAbpDistributedLock : IAbpDistributedLock, ITransientDependency
{
protected AbpDaprClientFactory DaprClientFactory { get; }
protected AbpDistributedLockDaprOptions DistributedLockDaprOptions { get; }
protected AbpDaprOptions DaprOptions { get; }
public DaprAbpDistributedLock(
AbpDaprClientFactory daprClientFactory,
IOptions<AbpDistributedLockDaprOptions> distributedLockDaprOptions,
IOptions<AbpDaprOptions> daprOptions)
{
DaprClientFactory = daprClientFactory;
DaprOptions = daprOptions.Value;
DistributedLockDaprOptions = distributedLockDaprOptions.Value;
}
public async Task<IAbpDistributedLockHandle> TryAcquireAsync(
string name,
TimeSpan timeout = default,
CancellationToken cancellationToken = default)
{
if (timeout == default)
{
timeout = DistributedLockDaprOptions.DefaultTimeout;
}
var daprClient = await DaprClientFactory.CreateAsync();
var lockResponse = await daprClient.Lock(
DistributedLockDaprOptions.StoreName,
name,
DaprOptions.AppId,
(int)timeout.TotalSeconds,
cancellationToken);
if (lockResponse == null || !lockResponse.Success)
{
return null;
}
return new DaprAbpDistributedLockHandle(lockResponse);
}
}

@ -0,0 +1,18 @@
using Dapr.Client;
namespace Volo.Abp.DistributedLocking.Dapr;
public class DaprAbpDistributedLockHandle : IAbpDistributedLockHandle
{
protected TryLockResponse LockResponse { get; }
public DaprAbpDistributedLockHandle(TryLockResponse lockResponse)
{
LockResponse = lockResponse;
}
public async ValueTask DisposeAsync()
{
await LockResponse.DisposeAsync();
}
}

@ -9,5 +9,4 @@ namespace Volo.Abp.DistributedLocking;
)]
public class AbpDistributedLockingModule : AbpModule
{
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj" />
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,11 @@
namespace Volo.Abp.EventBus.Dapr;
public class AbpDaprEventBusOptions
{
public string PubSubName { get; set; }
public AbpDaprEventBusOptions()
{
PubSubName = "pubsub";
}
}

@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Dapr;
using Volo.Abp.Modularity;
namespace Volo.Abp.EventBus.Dapr;
[DependsOn(
typeof(AbpEventBusModule),
typeof(AbpDaprModule)
)]
public class AbpEventBusDaprModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
context
.ServiceProvider
.GetRequiredService<DaprDistributedEventBus>()
.Initialize();
}
}

@ -0,0 +1,221 @@
using System.Collections.Concurrent;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Dapr;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace Volo.Abp.EventBus.Dapr;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDistributedEventBus), typeof(DaprDistributedEventBus))]
public class DaprDistributedEventBus : DistributedEventBusBase, ISingletonDependency
{
protected IDaprSerializer Serializer { get; }
protected AbpDaprEventBusOptions DaprEventBusOptions { get; }
protected AbpDaprClientFactory DaprClientFactory { get; }
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected ConcurrentDictionary<string, Type> EventTypes { get; }
public DaprDistributedEventBus(
IServiceScopeFactory serviceScopeFactory,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IOptions<AbpDistributedEventBusOptions> abpDistributedEventBusOptions,
IGuidGenerator guidGenerator,
IClock clock,
IEventHandlerInvoker eventHandlerInvoker,
IDaprSerializer serializer,
IOptions<AbpDaprEventBusOptions> daprEventBusOptions,
AbpDaprClientFactory daprClientFactory)
: base(serviceScopeFactory, currentTenant, unitOfWorkManager, abpDistributedEventBusOptions, guidGenerator, clock, eventHandlerInvoker)
{
Serializer = serializer;
DaprEventBusOptions = daprEventBusOptions.Value;
DaprClientFactory = daprClientFactory;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
}
public void Initialize()
{
SubscribeHandlers(AbpDistributedEventBusOptions.Handlers);
}
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
var handlerFactories = GetOrCreateHandlerFactories(eventType);
if (factory.IsInFactories(handlerFactories))
{
return NullDisposable.Instance;
}
handlerFactories.Add(factory);
return new EventHandlerFactoryUnregistrar(this, eventType, factory);
}
public override void Unsubscribe<TEvent>(Func<TEvent, Task> action)
{
Check.NotNull(action, nameof(action));
GetOrCreateHandlerFactories(typeof(TEvent))
.Locking(factories =>
{
factories.RemoveAll(
factory =>
{
var singleInstanceFactory = factory as SingleInstanceHandlerFactory;
if (singleInstanceFactory == null)
{
return false;
}
var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler<TEvent>;
if (actionHandler == null)
{
return false;
}
return actionHandler.Action == action;
});
});
}
public override void Unsubscribe(Type eventType, IEventHandler handler)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories =>
{
factories.RemoveAll(
factory =>
factory is SingleInstanceHandlerFactory &&
(factory as SingleInstanceHandlerFactory).HandlerInstance == handler
);
});
}
public override void Unsubscribe(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
}
public override void UnsubscribeAll(Type eventType)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
}
protected async override Task PublishToEventBusAsync(Type eventType, object eventData)
{
await PublishToDaprAsync(eventType, eventData);
}
protected override void AddToUnitOfWork(IUnitOfWork unitOfWork, UnitOfWorkEventRecord eventRecord)
{
unitOfWork.AddOrReplaceDistributedEvent(eventRecord);
}
protected override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)
{
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>();
foreach (var handlerFactory in HandlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key)))
{
handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value));
}
return handlerFactoryList.ToArray();
}
public async override Task PublishFromOutboxAsync(OutgoingEventInfo outgoingEvent, OutboxConfig outboxConfig)
{
await PublishToDaprAsync(outgoingEvent.EventName, Serializer.Deserialize(outgoingEvent.EventData, GetEventType(outgoingEvent.EventName)));
}
public async override Task PublishManyFromOutboxAsync(IEnumerable<OutgoingEventInfo> outgoingEvents, OutboxConfig outboxConfig)
{
var outgoingEventArray = outgoingEvents.ToArray();
foreach (var outgoingEvent in outgoingEventArray)
{
await PublishToDaprAsync(outgoingEvent.EventName, Serializer.Deserialize(outgoingEvent.EventData, GetEventType(outgoingEvent.EventName)));
}
}
public async override Task ProcessFromInboxAsync(IncomingEventInfo incomingEvent, InboxConfig inboxConfig)
{
var eventType = EventTypes.GetOrDefault(incomingEvent.EventName);
if (eventType == null)
{
return;
}
var eventData = Serializer.Deserialize(incomingEvent.EventData, eventType);
var exceptions = new List<Exception>();
await TriggerHandlersAsync(eventType, eventData, exceptions, inboxConfig);
if (exceptions.Any())
{
ThrowOriginalExceptions(eventType, exceptions);
}
}
protected override byte[] Serialize(object eventData)
{
return Serializer.Serialize(eventData);
}
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
{
return HandlerFactories.GetOrAdd(
eventType,
type =>
{
var eventName = EventNameAttribute.GetNameOrDefault(type);
EventTypes[eventName] = type;
return new List<IEventHandlerFactory>();
}
);
}
public Type GetEventType(string eventName)
{
return EventTypes.GetOrDefault(eventName);
}
protected virtual async Task PublishToDaprAsync(Type eventType, object eventData)
{
await PublishToDaprAsync(EventNameAttribute.GetNameOrDefault(eventType), eventData);
}
protected virtual async Task PublishToDaprAsync(string eventName, object eventData)
{
var client = await DaprClientFactory.CreateAsync();
await client.PublishEventAsync(pubsubName: DaprEventBusOptions.PubSubName, topicName: eventName, data: eventData);
}
private static bool ShouldTriggerEventForHandler(Type targetEventType, Type handlerEventType)
{
//Should trigger same type
if (handlerEventType == targetEventType)
{
return true;
}
//TODO: Support inheritance? But it does not support on subscription to RabbitMq!
//Should trigger for inherited types
if (handlerEventType.IsAssignableFrom(targetEventType))
{
return true;
}
return false;
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
<ProjectReference Include="..\Volo.Abp.Dapr\Volo.Abp.Dapr.csproj" />
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save