diff --git a/docs/en/API/Dynamic-CSharp-API-Clients.md b/docs/en/API/Dynamic-CSharp-API-Clients.md index 3c3c2ec26c..460623440f 100644 --- a/docs/en/API/Dynamic-CSharp-API-Clients.md +++ b/docs/en/API/Dynamic-CSharp-API-Clients.md @@ -69,7 +69,7 @@ public class MyClientAppModule : AbpModule } ```` -See the "RemoteServiceOptions" section below for more detailed configuration. +See the "AbpRemoteServiceOptions" section below for more detailed configuration. ## Usage @@ -104,7 +104,7 @@ While you can inject `IBookAppService` like above to use the client proxy, you c ## Configuration -### RemoteServiceOptions +### AbpRemoteServiceOptions `AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example: diff --git a/docs/en/Authorization.md b/docs/en/Authorization.md index 1bceda951f..888d907b59 100644 --- a/docs/en/Authorization.md +++ b/docs/en/Authorization.md @@ -339,10 +339,10 @@ A permission value provider should return one of the following values from the ` - `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return. - `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission. -Once a provider is defined, it should be added to the `PermissionOptions` as shown below: +Once a provider is defined, it should be added to the `AbpPermissionOptions` as shown below: ```csharp -Configure(options => +Configure(options => { options.ValueProviders.Add(); }); diff --git a/docs/en/Best-Practices/Entity-Framework-Core-Integration.md b/docs/en/Best-Practices/Entity-Framework-Core-Integration.md index db8117d798..f487f9d61d 100644 --- a/docs/en/Best-Practices/Entity-Framework-Core-Integration.md +++ b/docs/en/Best-Practices/Entity-Framework-Core-Integration.md @@ -107,10 +107,10 @@ public static class IdentityDbContextModelBuilderExtensions ```` * **Do** call `b.ConfigureByConvention();` for each entity mapping (as shown above). -* **Do** create a **configuration options** class by inheriting from the `ModelBuilderConfigurationOptions`. Example: +* **Do** create a **configuration options** class by inheriting from the `AbpModelBuilderConfigurationOptions`. Example: ````C# -public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions +public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions { public IdentityModelBuilderConfigurationOptions() : base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema) diff --git a/docs/en/Best-Practices/MongoDB-Integration.md b/docs/en/Best-Practices/MongoDB-Integration.md index 76b6e8561f..74a0e429f1 100644 --- a/docs/en/Best-Practices/MongoDB-Integration.md +++ b/docs/en/Best-Practices/MongoDB-Integration.md @@ -90,11 +90,11 @@ public static class AbpIdentityMongoDbContextExtensions } ``` -- **Do** create a **configuration options** class by inheriting from the `MongoModelBuilderConfigurationOptions`. Example: +- **Do** create a **configuration options** class by inheriting from the `AbpMongoModelBuilderConfigurationOptions`. Example: ```c# public class IdentityMongoModelBuilderConfigurationOptions - : MongoModelBuilderConfigurationOptions + : AbpMongoModelBuilderConfigurationOptions { public IdentityMongoModelBuilderConfigurationOptions() : base(AbpIdentityConsts.DefaultDbTablePrefix) diff --git a/docs/en/CLI.md b/docs/en/CLI.md index a347599135..f0fd3ae7bd 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -60,6 +60,7 @@ abp new Acme.BookStore * `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version. * `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://.zip`). * `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder. +* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true`. You can set your own connection string if you don't want to use the default. Be aware that the default database provider is `SQL Server`, therefore you can only enter connection string for SQL Server! ### add-package diff --git a/docs/en/Modules/Docs.md b/docs/en/Modules/Docs.md index db752ceb74..bbed28e8fe 100644 --- a/docs/en/Modules/Docs.md +++ b/docs/en/Modules/Docs.md @@ -134,7 +134,7 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => + Configure(options => { options.DefinitionProviders.Add(); }); diff --git a/docs/en/UI/Angular/Track-By-Service.md b/docs/en/UI/Angular/Track-By-Service.md index 71a98fda9d..e6b560a2eb 100644 --- a/docs/en/UI/Angular/Track-By-Service.md +++ b/docs/en/UI/Angular/Track-By-Service.md @@ -111,7 +111,3 @@ class DemoComponent { trackByTenantAccountId = trackByDeep('tenant', 'account', 'id'); } ``` - -## What's Next? - -* [Linked List (Doubly)](./Linked-List.md) \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Bundling-Minification.md b/docs/en/UI/AspNetCore/Bundling-Minification.md index 5af3f177d5..9d88a37135 100644 --- a/docs/en/UI/AspNetCore/Bundling-Minification.md +++ b/docs/en/UI/AspNetCore/Bundling-Minification.md @@ -246,10 +246,10 @@ public class MyPrismjsStyleExtension : BundleContributor } ```` -Then you can configure `BundleContributorOptions` to extend existing `PrismjsStyleBundleContributor`. +Then you can configure `AbpBundleContributorOptions` to extend existing `PrismjsStyleBundleContributor`. ````csharp -Configure(options => +Configure(options => { options .Extensions() diff --git a/docs/en/UI/Angular/Linked-List.md b/docs/en/UI/Common/Utils/Linked-List.md similarity index 93% rename from docs/en/UI/Angular/Linked-List.md rename to docs/en/UI/Common/Utils/Linked-List.md index 388c5963cd..971809e8a8 100644 --- a/docs/en/UI/Angular/Linked-List.md +++ b/docs/en/UI/Common/Utils/Linked-List.md @@ -130,7 +130,11 @@ list.addTail({ x: 3 }); // {"x":1} <-> {"x":2} <-> {"x":3} -list.addAfter({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x); +list.addAfter( + { x: 0 }, + 2, + (value, searchedValue) => value.x === searchedValue +); // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} ``` @@ -164,7 +168,11 @@ list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]); // {"x":1} <-> {"x":2} <-> {"x":3} -list.addManyAfter([{ x: 4 }, { x: 5 }], { x: 2 }, (v1, v2) => v1.x === v2.x); +list.addManyAfter( + [{ x: 4 }, { x: 5 }], + 2, + (value, searchedValue) => value.x === searchedValue +); // {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} ``` @@ -203,7 +211,11 @@ list.addTail({ x: 3 }); // {"x":1} <-> {"x":2} <-> {"x":3} -list.addBefore({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x); +list.addBefore( + { x: 0 }, + 2, + (value, searchedValue) => value.x === searchedValue +); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} ``` @@ -237,7 +249,11 @@ list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]); // {"x":1} <-> {"x":2} <-> {"x":3} -list.addManyBefore([{ x: 4 }, { x: 5 }], { x: 2 }, (v1, v2) => v1.x === v2.x); +list.addManyBefore( + [{ x: 4 }, { x: 5 }], + 2, + (value, searchedValue) => value.x === searchedValue +); // {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} ``` @@ -388,7 +404,9 @@ list.add({ x: 3 }).tail(); // {"x":1} <-> {"x":2} <-> {"x":3} -list.add({ x: 0 }).after({ x: 2 }, (v1, v2) => v1.x === v2.x); +list + .add({ x: 0 }) + .after(2, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} ``` @@ -429,7 +447,9 @@ list.add({ x: 3 }).tail(); // {"x":1} <-> {"x":2} <-> {"x":3} -list.add({ x: 0 }).before({ x: 2 }, (v1, v2) => v1.x === v2.x); +list + .add({ x: 0 }) + .before(2, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} ``` @@ -543,7 +563,9 @@ list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":2} <-> {"x":3} -list.addMany([{ x: 4 }, { x: 5 }]).after({ x: 2 }, (v1, v2) => v1.x === v2.x); +list + .addMany([{ x: 4 }, { x: 5 }]) + .after(2, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} ``` @@ -579,7 +601,9 @@ list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":2} <-> {"x":3} -list.addMany([{ x: 4 }, { x: 5 }]).before({ x: 2 }, (v1, v2) => v1.x === v2.x); +list + .addMany([{ x: 4 }, { x: 5 }]) + .before(2, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} ``` @@ -779,7 +803,7 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x); +list.dropByValue(0, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} ``` @@ -813,7 +837,7 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x); +list.dropByValue(0, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":3} ``` @@ -921,7 +945,9 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -list.drop().byValue({ x: 0 }, (v1, v2) => v1.x === v2.x); +list + .drop() + .byValue(0, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} ``` @@ -957,7 +983,9 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -list.drop().byValueAll({ x: 0 }, (v1, v2) => v1.x === v2.x); +list + .drop() + .byValueAll(0, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":3} ``` @@ -1145,11 +1173,11 @@ list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -const i0 = indexOf({ x: 1 }, (v1, v2) => v1.x === v2.x); -const i1 = indexOf({ x: 2 }, (v1, v2) => v1.x === v2.x); -const i2 = indexOf({ x: 3 }, (v1, v2) => v1.x === v2.x); -const i3 = indexOf({ x: 0 }, (v1, v2) => v1.x === v2.x); -const i4 = indexOf({ x: 4 }, (v1, v2) => v1.x === v2.x); +const i0 = indexOf(1, (value, searchedValue) => value.x === searchedValue); +const i1 = indexOf(2, (value, searchedValue) => value.x === searchedValue); +const i2 = indexOf(3, (value, searchedValue) => value.x === searchedValue); +const i3 = indexOf(0, (value, searchedValue) => value.x === searchedValue); +const i4 = indexOf(4, (value, searchedValue) => value.x === searchedValue); /* i0 === 0 diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index d5b24f4404..52258520f7 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -344,10 +344,15 @@ { "text": "TrackByService", "path": "UI/Angular/Track-By-Service.md" - }, + } + ] + }, + { + "text": "Common", + "items": [ { "text": "Linked List (Doubly)", - "path": "UI/Angular/Linked-List.md" + "path": "UI/Common/Utils/Linked-List.md" } ] } diff --git a/docs/zh-Hans/API/Dynamic-CSharp-API-Clients.md b/docs/zh-Hans/API/Dynamic-CSharp-API-Clients.md index 331e66c18e..eae86d6117 100644 --- a/docs/zh-Hans/API/Dynamic-CSharp-API-Clients.md +++ b/docs/zh-Hans/API/Dynamic-CSharp-API-Clients.md @@ -69,7 +69,7 @@ public class MyClientAppModule : AbpModule } ```` -查看下面的"RemoteServiceOptions"章节获取更多详细配置. +查看下面的"AbpRemoteServiceOptions"章节获取更多详细配置. ## 使用 @@ -104,7 +104,7 @@ public class MyService : ITransientDependency ## 配置 -### RemoteServiceOptions +### AbpRemoteServiceOptions 默认情况下`AbpRemoteServiceOptions`从`appsettings.json`获取.或者,你可以使用`Configure`方法来设置或重写它.如: diff --git a/docs/zh-Hans/Authorization.md b/docs/zh-Hans/Authorization.md index fd3ec72943..9886d07690 100644 --- a/docs/zh-Hans/Authorization.md +++ b/docs/zh-Hans/Authorization.md @@ -340,10 +340,10 @@ public class SystemAdminPermissionValueProvider : PermissionValueProvider * `PermissionGrantResult.Prohibited` 禁止授权用户,任何一个授权值提供程序返回了 `Prohibited`, 那么其他的提供程序返回的值都不再重要. * `PermissionGrantResult.Undefined` 代表当前无法确定是否授予或禁止权限, 返回`UnDefined`由其他权限值提供程序检查权限. -定义`Provider`后将其添加到 `PermissionOptions`,如下所示: +定义`Provider`后将其添加到 `AbpPermissionOptions`,如下所示: ````csharp -Configure(options => +Configure(options => { options.ValueProviders.Add(); }); diff --git a/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md b/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md index c74945ec01..b01f7eab28 100644 --- a/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md +++ b/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md @@ -106,10 +106,10 @@ public static class IdentityDbContextModelBuilderExtensions ```` * **推荐** 为每个Enttiy映射调用 `b.ConfigureByConvention();`(如上所示). -* **推荐** 通过继承 `ModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 例如: +* **推荐** 通过继承 `AbpModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 例如: ````C# -public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions +public class IdentityModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions { public IdentityModelBuilderConfigurationOptions() : base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema) diff --git a/docs/zh-Hans/Best-Practices/MongoDB-Integration.md b/docs/zh-Hans/Best-Practices/MongoDB-Integration.md index 4c71a58f90..b7b033b1fa 100644 --- a/docs/zh-Hans/Best-Practices/MongoDB-Integration.md +++ b/docs/zh-Hans/Best-Practices/MongoDB-Integration.md @@ -1,4 +1,4 @@ -## MongoDB 集成 +## MongoDB 集成 * **推荐** 为每个模块定义一个独立的 `MongoDbContext` 接口与实现类. @@ -90,11 +90,11 @@ public static class AbpIdentityMongoDbContextExtensions } ``` -- **推荐** 通过继承 `MongoModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 示例: +- **推荐** 通过继承 `AbpMongoModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 示例: ```c# public class IdentityMongoModelBuilderConfigurationOptions - : MongoModelBuilderConfigurationOptions + : AbpMongoModelBuilderConfigurationOptions { public IdentityMongoModelBuilderConfigurationOptions() : base(AbpIdentityConsts.DefaultDbTablePrefix) diff --git a/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md b/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md index c0f999f996..ebb402b273 100644 --- a/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md +++ b/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md @@ -115,7 +115,7 @@ public class MyWebModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => + Configure(options => { options .ScriptBundles @@ -153,7 +153,7 @@ public class MyWebExtensionModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => + Configure(options => { options .ScriptBundles @@ -191,7 +191,7 @@ public class MyExtensionGlobalStyleContributor : BundleContributor 然后你可以按照下面的代码使用这个贡献者: ````C# -services.Configure(options => +services.Configure(options => { options .ScriptBundles @@ -248,10 +248,10 @@ public class MyPrismjsStyleExtension : BundleContributor } ```` -然后你可以配置 `BundleContributorOptions` 去扩展已存在的 `PrismjsStyleBundleContributor`. +然后你可以配置 `AbpBundleContributorOptions` 去扩展已存在的 `PrismjsStyleBundleContributor`. ````csharp -Configure(options => +Configure(options => { options .Extensions() @@ -319,7 +319,7 @@ namespace MyCompany.MyProject 例如: ````c# -services.Configure(options => +services.Configure(options => { options .StyleBundles diff --git a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs index e78e9628c1..664e35e9b0 100644 --- a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs +++ b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs @@ -8,7 +8,7 @@ namespace AutoMapper { public static IMappingExpression MapExtraProperties( this IMappingExpression mappingExpression, - MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) + MappingPropertyDefinitionChecks? definitionChecks = null) where TDestination : IHasExtraProperties where TSource : IHasExtraProperties { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 222bfee307..8a0c21a254 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -218,6 +218,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(" abp new Acme.BookStore -t module --no-ui"); sb.AppendLine(" abp new Acme.BookStore -ts \"D:\\localTemplate\\abp\""); sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\""); + sb.AppendLine(" abp new Acme.BookStore --connection-string \"Server=myServerName\\myInstanceName;Database=myDatabase;User Id=myUsername;Password=myPassword\""); sb.AppendLine(""); sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs index e69d3f67c7..b78a05c684 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs @@ -70,9 +70,17 @@ namespace Volo.Abp.IdentityModel } var tokenResponse = await GetTokenResponse(discoveryResponse, configuration); + if (tokenResponse.IsError) { - throw new AbpException($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}"); + if (tokenResponse.ErrorDescription != null) + { + throw new AbpException($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}"); + } + + var rawError = tokenResponse.Raw; + var withoutInnerException = rawError.Split(new string[] { "" }, StringSplitOptions.RemoveEmptyEntries); + throw new AbpException(withoutInnerException[0]); } return tokenResponse.AccessToken; diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions.cs index 25156f298c..6b687fcf81 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using JetBrains.Annotations; using Volo.Abp.Data; @@ -25,7 +24,7 @@ namespace Volo.Abp.ObjectExtending public static void MapExtraPropertiesTo( [NotNull] this TSource source, [NotNull] TDestination destination, - MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) + MappingPropertyDefinitionChecks? definitionChecks = null) where TSource : IHasExtraProperties where TDestination : IHasExtraProperties { @@ -58,7 +57,7 @@ namespace Volo.Abp.ObjectExtending public static void MapExtraPropertiesTo( [NotNull] Dictionary sourceDictionary, [NotNull] Dictionary destinationDictionary, - MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) + MappingPropertyDefinitionChecks? definitionChecks = null) where TSource : IHasExtraProperties where TDestination : IHasExtraProperties { @@ -90,7 +89,7 @@ namespace Volo.Abp.ObjectExtending [NotNull] Type destinationType, [NotNull] Dictionary sourceDictionary, [NotNull] Dictionary destinationDictionary, - MappingPropertyDefinitionChecks definitionChecks = MappingPropertyDefinitionChecks.Both) + MappingPropertyDefinitionChecks? definitionChecks = null) { Check.AssignableTo(sourceType, nameof(sourceType)); Check.AssignableTo(destinationType, nameof(destinationType)); @@ -98,78 +97,120 @@ namespace Volo.Abp.ObjectExtending Check.NotNull(destinationDictionary, nameof(destinationDictionary)); var sourceObjectExtension = ObjectExtensionManager.Instance.GetOrNull(sourceType); - if (definitionChecks.HasFlag(MappingPropertyDefinitionChecks.Source) && - sourceObjectExtension == null) - { - return; - } - var destinationObjectExtension = ObjectExtensionManager.Instance.GetOrNull(destinationType); - if (definitionChecks.HasFlag(MappingPropertyDefinitionChecks.Destination) && - destinationObjectExtension == null) - { - return; - } - if (definitionChecks == MappingPropertyDefinitionChecks.None) + foreach (var keyValue in sourceDictionary) { - foreach (var keyValue in sourceDictionary) + if (CanMapProperty( + keyValue.Key, + sourceObjectExtension, + destinationObjectExtension, + definitionChecks)) { destinationDictionary[keyValue.Key] = keyValue.Value; } } - else if (definitionChecks == MappingPropertyDefinitionChecks.Source) - { - Debug.Assert(sourceObjectExtension != null, nameof(sourceObjectExtension) + " != null"); + } + + //TODO: Move these methods to a class like ObjectExtensionHelper + + public static bool CanMapProperty( + [NotNull] string propertyName, + MappingPropertyDefinitionChecks? definitionChecks = null) + { + return CanMapProperty( + typeof(TSource), + typeof(TDestination), + propertyName, + definitionChecks + ); + } + + public static bool CanMapProperty( + [NotNull] Type sourceType, + [NotNull] Type destinationType, + [NotNull] string propertyName, + MappingPropertyDefinitionChecks? definitionChecks = null) + { + Check.AssignableTo(sourceType, nameof(sourceType)); + Check.AssignableTo(destinationType, nameof(destinationType)); + Check.NotNull(propertyName, nameof(propertyName)); + + var sourceObjectExtension = ObjectExtensionManager.Instance.GetOrNull(sourceType); + var destinationObjectExtension = ObjectExtensionManager.Instance.GetOrNull(destinationType); + + return CanMapProperty( + propertyName, + sourceObjectExtension, + destinationObjectExtension, + definitionChecks); + } - foreach (var property in sourceObjectExtension.GetProperties()) + private static bool CanMapProperty( + [NotNull] string propertyName, + [CanBeNull] ObjectExtensionInfo sourceObjectExtension, + [CanBeNull] ObjectExtensionInfo destinationObjectExtension, + MappingPropertyDefinitionChecks? definitionChecks = null) + { + Check.NotNull(propertyName, nameof(propertyName)); + + if (definitionChecks != null) + { + if (definitionChecks.Value.HasFlag(MappingPropertyDefinitionChecks.Source)) { - if (!sourceDictionary.ContainsKey(property.Name)) + if (sourceObjectExtension == null) { - continue; + return false; } - destinationDictionary[property.Name] = sourceDictionary[property.Name]; + if (!sourceObjectExtension.HasProperty(propertyName)) + { + return false; + } } - } - else if (definitionChecks == MappingPropertyDefinitionChecks.Destination) - { - Debug.Assert(destinationObjectExtension != null, nameof(destinationObjectExtension) + " != null"); - foreach (var keyValue in sourceDictionary) + if (definitionChecks.Value.HasFlag(MappingPropertyDefinitionChecks.Destination)) { - if (!destinationObjectExtension.HasProperty(keyValue.Key)) + if (destinationObjectExtension == null) { - continue; + return false; } - destinationDictionary[keyValue.Key] = keyValue.Value; + if (!destinationObjectExtension.HasProperty(propertyName)) + { + return false; + } } + + return true; } - else if (definitionChecks == MappingPropertyDefinitionChecks.Both) + else { - Debug.Assert(sourceObjectExtension != null, nameof(sourceObjectExtension) + " != null"); - Debug.Assert(destinationObjectExtension != null, nameof(destinationObjectExtension) + " != null"); + var sourcePropertyDefinition = sourceObjectExtension?.GetPropertyOrNull(propertyName); + var destinationPropertyDefinition = destinationObjectExtension?.GetPropertyOrNull(propertyName); - foreach (var property in sourceObjectExtension.GetProperties()) + if (sourcePropertyDefinition != null) { - if (!sourceDictionary.ContainsKey(property.Name)) + if (destinationPropertyDefinition != null) { - continue; + return true; } - if (!destinationObjectExtension.HasProperty(property.Name)) + if (sourcePropertyDefinition.CheckPairDefinitionOnMapping == false) { - continue; + return true; } - - destinationDictionary[property.Name] = sourceDictionary[property.Name]; } - } - else - { - throw new NotImplementedException(definitionChecks + " was not implemented!"); + else if (destinationPropertyDefinition != null) + { + if (destinationPropertyDefinition.CheckPairDefinitionOnMapping == false) + { + return true; + } + } + + return false; } } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs index af5d0c2ce4..2edae9a3f8 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs @@ -15,6 +15,20 @@ namespace Volo.Abp.ObjectExtending [NotNull] public Type Type { get; } + /// + /// Indicates whether to check the other side of the object mapping + /// if it explicitly defines the property. This property is used in; + /// + /// * .MapExtraPropertiesTo() extension method. + /// * .MapExtraProperties() configuration for the AutoMapper. + /// + /// It this is true, these methods check if the mapping object + /// has defined the property using the . + /// + /// Default: null (unspecified, uses the default logic). + /// + public bool? CheckPairDefinitionOnMapping { get; set; } + [NotNull] public Dictionary Configuration { get; } diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/AbpObjectExtendingTestModule.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/AbpObjectExtendingTestModule.cs index 6dcee4d5b2..470bb547e5 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/AbpObjectExtendingTestModule.cs +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/AbpObjectExtendingTestModule.cs @@ -19,6 +19,7 @@ namespace Volo.Abp.ObjectExtending ObjectExtensionManager.Instance .AddOrUpdateProperty("Name") .AddOrUpdateProperty("Age") + .AddOrUpdateProperty("NoPairCheck", options => options.CheckPairDefinitionOnMapping = false) .AddOrUpdateProperty("Name") .AddOrUpdateProperty("ChildCount"); }); diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions_Tests.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions_Tests.cs index a51d9514b2..7ba213b81c 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions_Tests.cs +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/HasExtraPropertiesObjectExtendingExtensions_Tests.cs @@ -16,7 +16,8 @@ namespace Volo.Abp.ObjectExtending .SetProperty("Name", "John") .SetProperty("Age", 42) .SetProperty("ChildCount", 2) - .SetProperty("Sex", "male"); + .SetProperty("Sex", "male") + .SetProperty("NoPairCheck", "test-value"); _personDto = new ExtensibleTestPersonDto() .SetProperty("ExistingDtoProperty", "existing-value"); @@ -31,6 +32,7 @@ namespace Volo.Abp.ObjectExtending _personDto.HasProperty("Age").ShouldBeFalse(); //Not defined on the destination _personDto.HasProperty("ChildCount").ShouldBeFalse(); //Not defined in the source _personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes + _personDto.GetProperty("NoPairCheck").ShouldBe("test-value"); //CheckPairDefinitionOnMapping = false _personDto.GetProperty("ExistingDtoProperty").ShouldBe("existing-value"); //Should not clear existing values } diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs index 56a9653b73..d9b0a4c636 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/RawFileDto.cs @@ -1,7 +1,22 @@ -namespace Volo.Blogging.Files +using System.Security.Cryptography; +using Volo.Abp.Threading; + +namespace Volo.Blogging.Files { public class RawFileDto { public byte[] Bytes { get; set; } + + public bool IsFileEmpty => Bytes == null || Bytes.Length == 0; + + public RawFileDto() + { + + } + + public static RawFileDto EmptyResult() + { + return new RawFileDto() {Bytes = new byte[0]}; + } } } \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs index 4673f5c6d1..cdfd4cb836 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.Validation; @@ -12,9 +13,11 @@ namespace Volo.Blogging.Files public class FileAppService : BloggingAppServiceBase, IFileAppService { public BlogFileOptions Options { get; } + private readonly ILogger _logger; - public FileAppService(IOptions options) + public FileAppService(IOptions options, ILogger logger) { + _logger = logger; Options = options.Value; } @@ -35,12 +38,13 @@ namespace Volo.Blogging.Files var filePath = Path.Combine(Options.FileUploadLocalFolder, name); - return Task.FromResult( - new RawFileDto - { - Bytes = File.ReadAllBytes(filePath) - } - ); + if (File.Exists(filePath)) + { + return Task.FromResult(new RawFileDto {Bytes = File.ReadAllBytes(filePath)}); + } + + _logger.LogError($"Cannot find the file {filePath}"); + return Task.FromResult(RawFileDto.EmptyResult()); } public virtual Task CreateAsync(FileUploadInputDto input) diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 9e81892e58..80adfa3d3e 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -34,6 +34,7 @@ "@abp/ng.tenant-management.config": "~2.3.0", "@abp/ng.theme.basic": "~2.3.0", "@abp/ng.theme.shared": "^2.3.0", + "@abp/utils": "~2.3.0", "@angular-builders/jest": "^8.2.0", "@angular-devkit/build-angular": "~0.803.21", "@angular-devkit/build-ng-packagr": "~0.803.21", diff --git a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts index 3ca180a5df..1a8905202b 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts @@ -1,10 +1,10 @@ -import { getInitialData, localeInitializer } from '../utils'; -import { Injector } from '@angular/core'; -import { Spectator, createComponentFactory } from '@ngneat/spectator/jest'; -import { Component } from '@angular/core'; +import { Component, Injector } from '@angular/core'; +import { createComponentFactory, Spectator } from '@ngneat/spectator/jest'; import { Store } from '@ngxs/store'; +import { OAuthService } from 'angular-oauth2-oidc'; import { of } from 'rxjs'; import { GetAppConfiguration } from '../actions'; +import { getInitialData, localeInitializer } from '../utils'; @Component({ selector: 'abp-dummy', @@ -14,7 +14,10 @@ export class DummyComponent {} describe('InitialUtils', () => { let spectator: Spectator; - const createComponent = createComponentFactory({ component: DummyComponent, mocks: [Store] }); + const createComponent = createComponentFactory({ + component: DummyComponent, + mocks: [Store], + }); beforeEach(() => (spectator = createComponent())); @@ -25,7 +28,8 @@ describe('InitialUtils', () => { const store = spectator.get(Store); const dispatchSpy = jest.spyOn(store, 'dispatch'); - injectorSpy.mockReturnValue(store); + injectorSpy.mockReturnValueOnce(store); + injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => false }); dispatchSpy.mockReturnValue(of('test')); expect(typeof getInitialData(injector)).toBe('function'); @@ -34,6 +38,23 @@ describe('InitialUtils', () => { }); }); + describe('#checkAccessToken', () => { + test('should call logOut fn of OAuthService when token is valid and current user not found', async () => { + const injector = spectator.get(Injector); + const injectorSpy = jest.spyOn(injector, 'get'); + const store = spectator.get(Store); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const logOutFn = jest.fn(); + + injectorSpy.mockReturnValueOnce(store); + injectorSpy.mockReturnValueOnce({ hasValidAccessToken: () => true, logOut: logOutFn }); + dispatchSpy.mockReturnValue(of({ currentUser: { id: null } })); + + getInitialData(injector)(); + expect(logOutFn).toHaveBeenCalled(); + }); + }); + describe('#localeInitializer', () => { test('should resolve registerLocale', async () => { const injector = spectator.get(Injector); diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index 2043b1e8b6..04a5a1ca83 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -3,17 +3,31 @@ import { Injector } from '@angular/core'; import { Store } from '@ngxs/store'; import { GetAppConfiguration } from '../actions/config.actions'; import differentLocales from '../constants/different-locales'; +import { ApplicationConfiguration } from '../models/application-configuration'; +import { tap } from 'rxjs/operators'; +import { ConfigState } from '../states/config.state'; +import { OAuthService } from 'angular-oauth2-oidc'; export function getInitialData(injector: Injector) { const fn = () => { const store: Store = injector.get(Store); - return store.dispatch(new GetAppConfiguration()).toPromise(); + return store + .dispatch(new GetAppConfiguration()) + .pipe(tap(res => checkAccessToken(store, injector))) + .toPromise(); }; return fn; } +function checkAccessToken(store: Store, injector: Injector) { + const oAuth = injector.get(OAuthService); + if (oAuth.hasValidAccessToken() && !store.selectSnapshot(ConfigState.getDeep('currentUser.id'))) { + oAuth.logOut(); + } +} + export function localeInitializer(injector: Injector) { const fn = () => { const store: Store = injector.get(Store); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html index 9d953dbf5e..ecb3509ce2 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.html @@ -195,8 +195,19 @@ -