From 3c6e1345f27d1dfc3636fa052230a44c5faccc53 Mon Sep 17 00:00:00 2001 From: bnymncoskuner Date: Fri, 13 Nov 2020 15:22:28 +0300 Subject: [PATCH 01/15] docs: write migration guide v4 for angular --- docs/en/Migration-Guides/Abp-4_0-Angular.md | 77 +++++++++++++++++++-- docs/en/UI/Angular/Environment.md | 2 +- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/docs/en/Migration-Guides/Abp-4_0-Angular.md b/docs/en/Migration-Guides/Abp-4_0-Angular.md index 60f6e46de5..ef49dd88c9 100644 --- a/docs/en/Migration-Guides/Abp-4_0-Angular.md +++ b/docs/en/Migration-Guides/Abp-4_0-Angular.md @@ -1,11 +1,80 @@ # Angular UI 3.3 to 4.0 Migration Guide -### Removed the Angular Account Module Public UI +## Angular v11 -Angular UI is using the Authorization Code Flow to authenticate since the version 3.1.0 by default. Starting from the version 4.0, this is becoming the only option, because it is the recommended way of authenticating SPAs. +The new ABP Angular UI is based on Angular v11 and TypeScript v4. The difference between v10 and v11 is non-breaking so you do not have to update right away but it is recommended. Nevertheless, ABP modules will keep working with Angular v10. Therefore, if your project is Angular v10, you do not need to update to Angular 11. The update is usually very easy though. + +You can read more about Angular v11 [here](https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7) + +## **Breaking Changes** + +### **Localization** + +Prior to ABP 4.x, we'd handled what language files should be created to load them lazily. However, this made it impossible to add new language files (to be lazily loaded) for our users. With ABP 4.x, we enabled an option to pass a function to `CoreModule`. + +You can find the related issue [here](https://github.com/abpframework/abp/issues/6066) +Also, please refer to [the docs](https://docs.abp.io/en/abp/latest/UI/Angular/Localization#registering-a-new-locale) for how to implement this. + +### **Removed the Angular Account Module Public UI** + +With ABP 4.x, we have retired `@abp/ng.account`, it is no longer a part of our framework. + +You can find the related issue [here](https://github.com/abpframework/abp/issues/5652) + +Angular UI is using the Authorization Code Flow to authenticate since version 3.1.0 by default. Starting from version 4.0, this is becoming the only option, because it is the recommended way of authenticating SPAs. If you haven't done it yet, see [this post](https://blog.abp.io/abp/ABP-Framework-v3.1-RC-Has-Been-Released) to change the authentication of your application. -### Removed the SessionState +### State Management + +In the ABP Framework, we've been using `NGXS` for state management. However, we've decided that the ABP Framework should be agnostic with regard to state management. Our users should be able to handle the state in any way they prefer. They should be able to use any library other than `NGXS` or no library at all. That's why we have created our internal store in version 3.2. It is a simple utility class that employs `BehaviorSubject` internally. + +You can examine it [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts) + +With version 4.0, we will keep utilizing our `InternalStore` instead of `@ngxs/store` in our services and move away from `@ngxs/store`. We plan to remove any dependency of `ngxs` by version 5.0. + +With this in mind, we've already deprecated some services and implemented some breaking changes. + +#### Removed the `SessionState` + +Use `SessionStateService` instead of the `SessionState`. See [this issue](https://github.com/abpframework/abp/issues/5606) for details. + +#### Deprecated the `ConfigState` + +`ConfigState` is now deprecated and should not be used. +Moving away from the global store, we create small services with a single responsibility. There are two new services available in version 4.0 which are `EnvironmentService` and `PermissionService`. + +See [the related issue](https://github.com/abpframework/abp/issues/6154) + +Please refer to the following docs for detail information and examples +- [`ConfigStateService`](../UI/Angular/Config-State-Service.md) +- [`EnvironmentService`](../UI/Angular/Environment.md#EnvironmentService) +- [`PermissionService`](../UI/Angular/Permission-Management.md#) + +### Deprecated Interfaces + + Some interfaces have long been marked as deprecated and now they are removed. + +- Removed replaceable components state. +- Removed legacy identity types and service. +- Removed legacy tenant management types and service. +- Removed legacy feature management types and services. +- Removed legacy permission management types and service. + +### Deprecated commercial interfaces +- Removed legacy audit logging types and services. +- Removed legacy identity types and services. +- Removed legacy language management types and services. +- Removed legacy saas types and services. + +### Identity Server [COMMERCIAL] + +With the new version of Identity Server, there happened some breaking changes in the backend (also in the database). We've implemented those in the Angular UI. +If you are just using the package `@volo/abp.ng.identity-server` as is, you will not need to do anything. +However, there are a couple of breaking changes we need to mention. + +- As we have stated above, we want to remove the dependency of `Ngxs`. Thus, we have deleted all of the actions defined in `identity-server.actions`. Those actions are not needed anymore and the state is managed locally. With the actions gone, `IdentityServerStateService` became unused and got deleted as well. + +- `ApiScope` is also available as a new entity (It was part of `ApiResource` before). It provides tokens for entity prop, entity actions, toolbar, edit and create form contributors like the existing ones which are `Client`, `IdentityResource` and `ApiResource` -Use `SessionStateService` instead of the `SessionState`. See [this issue](https://github.com/abpframework/abp/issues/5606) for details. \ No newline at end of file +- There were some deprecated interfaces within `IdentityServer` namespace. Those are no longer being used, instead their replacements were generated by `ABP Cli` using `generate-proxy` command. diff --git a/docs/en/UI/Angular/Environment.md b/docs/en/UI/Angular/Environment.md index 37ef585308..8c6fb28ce7 100644 --- a/docs/en/UI/Angular/Environment.md +++ b/docs/en/UI/Angular/Environment.md @@ -102,7 +102,7 @@ export interface RemoteEnv { * `method`: HTTP method to be used when retrieving environment config. Default: `GET` * `headers`: If extra headers are needed for the request, it can be set through this field. -## EnvironmentService +## EnvironmentService ` EnvironmentService` is a singleton service, i.e. provided in root level of your application, and keeps the environment in the internal store. From e7825023fa07639e7a0dfe082c56ad998c217be9 Mon Sep 17 00:00:00 2001 From: Bunyamin Coskuner Date: Fri, 13 Nov 2020 16:21:35 +0300 Subject: [PATCH 02/15] docs: update links on migration guide Co-authored-by: Mehmet Erim <34455572+mehmet-erim@users.noreply.github.com> --- docs/en/Migration-Guides/Abp-4_0-Angular.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/Migration-Guides/Abp-4_0-Angular.md b/docs/en/Migration-Guides/Abp-4_0-Angular.md index ef49dd88c9..bf357a2a53 100644 --- a/docs/en/Migration-Guides/Abp-4_0-Angular.md +++ b/docs/en/Migration-Guides/Abp-4_0-Angular.md @@ -47,9 +47,9 @@ Moving away from the global store, we create small services with a single respon See [the related issue](https://github.com/abpframework/abp/issues/6154) Please refer to the following docs for detail information and examples -- [`ConfigStateService`](../UI/Angular/Config-State-Service.md) -- [`EnvironmentService`](../UI/Angular/Environment.md#EnvironmentService) -- [`PermissionService`](../UI/Angular/Permission-Management.md#) +- [`ConfigStateService`](../UI/Angular/Config-State-Service) +- [`EnvironmentService`](../UI/Angular/Environment#EnvironmentService) +- [`PermissionService`](../UI/Angular/Permission-Management#) ### Deprecated Interfaces From 2944abcf6359807dd29ff3ff3f15ed20d98e521a Mon Sep 17 00:00:00 2001 From: Bunyamin Coskuner Date: Fri, 13 Nov 2020 16:22:49 +0300 Subject: [PATCH 03/15] docs: improvements on migration guide for angular Co-authored-by: Mehmet Erim <34455572+mehmet-erim@users.noreply.github.com> --- docs/en/Migration-Guides/Abp-4_0-Angular.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Migration-Guides/Abp-4_0-Angular.md b/docs/en/Migration-Guides/Abp-4_0-Angular.md index bf357a2a53..1165317935 100644 --- a/docs/en/Migration-Guides/Abp-4_0-Angular.md +++ b/docs/en/Migration-Guides/Abp-4_0-Angular.md @@ -10,7 +10,7 @@ You can read more about Angular v11 [here](https://blog.angular.io/version-11-of ### **Localization** -Prior to ABP 4.x, we'd handled what language files should be created to load them lazily. However, this made it impossible to add new language files (to be lazily loaded) for our users. With ABP 4.x, we enabled an option to pass a function to `CoreModule`. +Prior to ABP 4.x, we'd handled what locale files of Angular should be created to load them lazily. However, this made it impossible to add new locale files (to be lazily loaded) for our users. With ABP 4.x, we enabled an option to pass a function to `CoreModule`. You can find the related issue [here](https://github.com/abpframework/abp/issues/6066) Also, please refer to [the docs](https://docs.abp.io/en/abp/latest/UI/Angular/Localization#registering-a-new-locale) for how to implement this. From 7b916f4627dd0a86bf290213608cd32d69119b9b Mon Sep 17 00:00:00 2001 From: Bunyamin Coskuner Date: Fri, 13 Nov 2020 16:22:59 +0300 Subject: [PATCH 04/15] docs: improvements on migration guide for angular Co-authored-by: Mehmet Erim <34455572+mehmet-erim@users.noreply.github.com> --- docs/en/Migration-Guides/Abp-4_0-Angular.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Migration-Guides/Abp-4_0-Angular.md b/docs/en/Migration-Guides/Abp-4_0-Angular.md index 1165317935..9f3d26e568 100644 --- a/docs/en/Migration-Guides/Abp-4_0-Angular.md +++ b/docs/en/Migration-Guides/Abp-4_0-Angular.md @@ -27,7 +27,7 @@ If you haven't done it yet, see [this post](https://blog.abp.io/abp/ABP-Framewor ### State Management -In the ABP Framework, we've been using `NGXS` for state management. However, we've decided that the ABP Framework should be agnostic with regard to state management. Our users should be able to handle the state in any way they prefer. They should be able to use any library other than `NGXS` or no library at all. That's why we have created our internal store in version 3.2. It is a simple utility class that employs `BehaviorSubject` internally. +In the ABP Angular UI, we've been using `NGXS` for state management. However, we've decided that the Angular UI should be agnostic with regard to state management. Our users should be able to handle the state in any way they prefer. They should be able to use any library other than `NGXS` or no library at all. That's why we have created our internal store in version 3.2. It is a simple utility class that employs `BehaviorSubject` internally. You can examine it [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts) From d2cf1834dfbaba0c939c1c86beaccf7aa8b2c211 Mon Sep 17 00:00:00 2001 From: bnymncoskuner Date: Fri, 13 Nov 2020 16:41:53 +0300 Subject: [PATCH 05/15] docs: improvements on migration guide v4 for ng --- docs/en/Migration-Guides/Abp-4_0-Angular.md | 51 +++++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/docs/en/Migration-Guides/Abp-4_0-Angular.md b/docs/en/Migration-Guides/Abp-4_0-Angular.md index 9f3d26e568..6095a0f447 100644 --- a/docs/en/Migration-Guides/Abp-4_0-Angular.md +++ b/docs/en/Migration-Guides/Abp-4_0-Angular.md @@ -12,12 +12,38 @@ You can read more about Angular v11 [here](https://blog.angular.io/version-11-of Prior to ABP 4.x, we'd handled what locale files of Angular should be created to load them lazily. However, this made it impossible to add new locale files (to be lazily loaded) for our users. With ABP 4.x, we enabled an option to pass a function to `CoreModule`. +The quickest solution is as follows: + +```typescript +// app.module.ts + +import { registerLocale } from '@abp/ng.core/locale'; +// or +// import { registerLocale } from '@volo/abp.ng.language-management/locale'; +// if you have commercial license + + +@NgModule({ +imports: [ + // ... + CoreModule.forRoot({ + // ...other options, + registerLocaleFn: registerLocale() + }), + //... +] +export class AppModule {} +``` + You can find the related issue [here](https://github.com/abpframework/abp/issues/6066) -Also, please refer to [the docs](https://docs.abp.io/en/abp/latest/UI/Angular/Localization#registering-a-new-locale) for how to implement this. +Also, please refer to [the docs](https://docs.abp.io/en/abp/latest/UI/Angular/Localization#registering-a-new-locale) for more information. ### **Removed the Angular Account Module Public UI** -With ABP 4.x, we have retired `@abp/ng.account`, it is no longer a part of our framework. +With ABP 4.x, we have retired `@abp/ng.account`, it is no longer a part of our framework. There won't be any newer versions of this package as well. Therefore, you can delete anything related to this package. +There should be a config in `app-routing.module` for path `account` and `AccountConfigModule` import in `app.module` + +However, if you are using the commercial version of this package, a.k.a `@volo/abp.ng.account`, this package will continue to exist because it contains `AccountAdminModule` which is still being maintained and developed. You only need to delete the route config from `app-routing.module` You can find the related issue [here](https://github.com/abpframework/abp/issues/5652) @@ -31,7 +57,7 @@ In the ABP Angular UI, we've been using `NGXS` for state management. However, we You can examine it [here](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/utils/internal-store-utils.ts) -With version 4.0, we will keep utilizing our `InternalStore` instead of `@ngxs/store` in our services and move away from `@ngxs/store`. We plan to remove any dependency of `ngxs` by version 5.0. +With version 4.0, we will keep utilizing our `InternalStore` instead of `@ngxs/store` in our services and move away from `@ngxs/store`. We plan to remove any dependency of `NGXS` by version 5.0. With this in mind, we've already deprecated some services and implemented some breaking changes. @@ -42,6 +68,21 @@ Use `SessionStateService` instead of the `SessionState`. See [this issue](https: #### Deprecated the `ConfigState` `ConfigState` is now deprecated and should not be used. + +`ConfigState` reference removed from `CoreModule`. If you want to use the `ConfigState` (not recommended), you should pass the state to `NgxsModule` as shown below: + +```typescript +//app.module.ts + +import { ConfigState } from '@abp/ng.core'; + +// ... + +imports: [ + NgxsModule.forRoot([ConfigState]), +// ... +``` + Moving away from the global store, we create small services with a single responsibility. There are two new services available in version 4.0 which are `EnvironmentService` and `PermissionService`. See [the related issue](https://github.com/abpframework/abp/issues/6154) @@ -73,8 +114,8 @@ With the new version of Identity Server, there happened some breaking changes in If you are just using the package `@volo/abp.ng.identity-server` as is, you will not need to do anything. However, there are a couple of breaking changes we need to mention. -- As we have stated above, we want to remove the dependency of `Ngxs`. Thus, we have deleted all of the actions defined in `identity-server.actions`. Those actions are not needed anymore and the state is managed locally. With the actions gone, `IdentityServerStateService` became unused and got deleted as well. +- As we have stated above, we want to remove the dependency of `NGXS`. Thus, we have deleted all of the actions defined in `identity-server.actions`. Those actions are not needed anymore and the state is managed locally. With the actions gone, `IdentityServerStateService` became unused and got deleted as well. - `ApiScope` is also available as a new entity (It was part of `ApiResource` before). It provides tokens for entity prop, entity actions, toolbar, edit and create form contributors like the existing ones which are `Client`, `IdentityResource` and `ApiResource` -- There were some deprecated interfaces within `IdentityServer` namespace. Those are no longer being used, instead their replacements were generated by `ABP Cli` using `generate-proxy` command. +- There were some deprecated interfaces within the `IdentityServer` namespace. Those are no longer being used, instead, their replacements were generated by `ABP Cli` using the `generate-proxy` command. From 243bfd6fc0877de08b5ad61942f27c2f3f435f00 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 13 Nov 2020 22:50:34 +0800 Subject: [PATCH 06/15] Handling the case where JSON is null or empty. Resolve #6191 --- .../JsonConverters/AbpDateTimeConverter.cs | 24 +++- .../AbpHasExtraPropertiesJsonConverter.cs | 20 ++- .../AbpNullableDateTimeConverter.cs | 21 ++- ...pSystemTextJsonSerializerProvider_Tests.cs | 131 +++++++++++++++--- .../JsonConverters/EntityJsonConverter.cs | 23 +-- 5 files changed, 183 insertions(+), 36 deletions(-) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs index a87add029e..e35589830d 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.Options; @@ -20,7 +21,28 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return _clock.Normalize(reader.GetDateTime()); + if (!_options.DefaultDateTimeFormat.IsNullOrWhiteSpace()) + { + if (reader.TokenType == JsonTokenType.String) + { + var s = reader.GetString(); + if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var d1)) + { + return _clock.Normalize(d1); + } + + throw new JsonException($"{s} cannot parse to DateTime({_options.DefaultDateTimeFormat})!"); + } + + throw new JsonException("reader TokenType is not String!"); + } + + if (reader.TryGetDateTime(out var d2)) + { + return _clock.Normalize(d2); + } + + throw new JsonException("reader can't get datetime!"); } public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs index 40227dd5c3..e5e3f5e187 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs @@ -16,15 +16,21 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters x.GetType() == typeof(AbpHasExtraPropertiesJsonConverterFactory)); var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var extensibleObject = JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions); + if (rootElement.ValueKind == JsonValueKind.Object) + { + var extensibleObject = JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions); - var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x => - x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)) - .Value.GetRawText(); - var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties, typeof(ExtraPropertyDictionary), newOptions); - ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); + var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)); + if (extraProperties.Value.ValueKind == JsonValueKind.Object) + { + var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties.Value.GetRawText(), typeof(ExtraPropertyDictionary), newOptions); + ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); + } - return extensibleObject; + return extensibleObject; + } + + throw new JsonException("RootElement ValueKind is not Object!"); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs index bad650b4d8..3a603af994 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.Options; @@ -20,9 +21,25 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TryGetDateTime(out var dateTime)) + if (!_options.DefaultDateTimeFormat.IsNullOrWhiteSpace()) { - return _clock.Normalize(dateTime); + if (reader.TokenType == JsonTokenType.String) + { + var s = reader.GetString(); + if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var d1)) + { + return _clock.Normalize(d1); + } + + throw new JsonException($"{s} cannot parse to DateTime({_options.DefaultDateTimeFormat})!"); + } + + throw new JsonException("reader TokenType is not String!"); + } + + if (reader.TryGetDateTime(out var d2)) + { + return _clock.Normalize(d2); } return null; diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index da49f7ebc6..3c32a99dec 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -1,4 +1,6 @@ -using Shouldly; +using System; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; using Volo.Abp.Data; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.ObjectExtending; @@ -8,23 +10,23 @@ namespace Volo.Abp.Json { public class AbpSystemTextJsonSerializerProvider_Tests : AbpJsonTestBase { - private readonly AbpSystemTextJsonSerializerProvider _jsonSerializer; + protected AbpSystemTextJsonSerializerProvider JsonSerializer; public AbpSystemTextJsonSerializerProvider_Tests() { - _jsonSerializer = GetRequiredService(); + JsonSerializer = GetRequiredService(); } [Fact] public void Serialize_Deserialize_With_Boolean() { var json = "{\"name\":\"abp\",\"IsDeleted\":\"fAlSe\"}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.IsDeleted.ShouldBeFalse(); file.IsDeleted = false; - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"isDeleted\":false}"); } @@ -32,19 +34,19 @@ namespace Volo.Abp.Json public void Serialize_Deserialize_With_Nullable_Boolean() { var json = "{\"name\":\"abp\",\"IsDeleted\":null}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.IsDeleted.ShouldBeNull(); - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"isDeleted\":null}"); json = "{\"name\":\"abp\",\"IsDeleted\":\"true\"}"; - file = _jsonSerializer.Deserialize(json); + file = JsonSerializer.Deserialize(json); file.IsDeleted.ShouldNotBeNull(); file.IsDeleted.Value.ShouldBeTrue(); - newJson = _jsonSerializer.Serialize(file); + newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"isDeleted\":true}"); } @@ -52,11 +54,11 @@ namespace Volo.Abp.Json public void Serialize_Deserialize_With_Enum() { var json = "{\"name\":\"abp\",\"type\":\"Exe\"}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.Type.ShouldBe(FileType.Exe); - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"type\":2}"); } @@ -64,35 +66,73 @@ namespace Volo.Abp.Json public void Serialize_Deserialize_With_Nullable_Enum() { var json = "{\"name\":\"abp\",\"type\":null}"; - var file = _jsonSerializer.Deserialize(json); + var file = JsonSerializer.Deserialize(json); file.Name.ShouldBe("abp"); file.Type.ShouldBeNull(); - var newJson = _jsonSerializer.Serialize(file); + var newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"type\":null}"); json = "{\"name\":\"abp\",\"type\":\"Exe\"}"; - file = _jsonSerializer.Deserialize(json); + file = JsonSerializer.Deserialize(json); file.Type.ShouldNotBeNull(); file.Type.ShouldBe(FileType.Exe); - newJson = _jsonSerializer.Serialize(file); + newJson = JsonSerializer.Serialize(file); newJson.ShouldBe("{\"name\":\"abp\",\"type\":2}"); } - [Fact] public void Serialize_Deserialize_ExtensibleObject() { var json = "{\"name\":\"test\",\"extraProperties\":{\"One\":\"123\",\"Two\":456}}"; - var extensibleObject = _jsonSerializer.Deserialize(json); + var extensibleObject = JsonSerializer.Deserialize(json); extensibleObject.GetProperty("One").ShouldBe("123"); extensibleObject.GetProperty("Two").ShouldBe(456); - var newJson = _jsonSerializer.Serialize(extensibleObject); + var newJson = JsonSerializer.Serialize(extensibleObject); newJson.ShouldBe(json); } + [Fact] + public void Serialize_Deserialize_ExtensibleObject_With_ExtraProperties_String() + { + var json = "{\"name\":\"test\"}"; + var extensibleObject = JsonSerializer.Deserialize(json); + extensibleObject.ExtraProperties.ShouldNotBeNull(); + extensibleObject.ExtraProperties.ShouldBeEmpty(); + } + + [Fact] + public void Serialize_Deserialize_With_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":\"2020-11-20T00:00:00\"}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.Year.ShouldBe(2020); + file.CreationTime.Month.ShouldBe(11); + file.CreationTime.Day.ShouldBe(20); + } + + [Fact] + public void Serialize_Deserialize_With_Nullable_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":null}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\",\"creationTime\":\"2020-11-20T00:00:00\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldNotBeNull(); + + file.CreationTime.Value.Year.ShouldBe(2020); + file.CreationTime.Value.Month.ShouldBe(11); + file.CreationTime.Value.Day.ShouldBe(20); + } + class TestExtensibleObjectClass : ExtensibleObject { public string Name { get; set; } @@ -131,5 +171,60 @@ namespace Volo.Abp.Json Zip = 0, Exe = 2 } + + protected class FileWithDatetime + { + public string Name { get; set; } + + public DateTime CreationTime { get; set; } + } + + protected class FileWithNullableDatetime + { + public string Name { get; set; } + + public DateTime? CreationTime { get; set; } + } + } + + public class FormatAbpSystemTextJsonSerializerProvider_Tests : AbpSystemTextJsonSerializerProvider_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.DefaultDateTimeFormat = "yyyy*MM*dd"; + }); + } + + [Fact] + public void Serialize_Deserialize_With_Format_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":\"2020*11*20\"}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.Year.ShouldBe(2020); + file.CreationTime.Month.ShouldBe(11); + file.CreationTime.Day.ShouldBe(20); + } + + [Fact] + public void Serialize_Deserialize_With_Nullable_Format_Datetime() + { + var json = "{\"name\":\"abp\",\"creationTime\":null}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldBeNull(); + + json = "{\"name\":\"abp\",\"creationTime\":\"2020*11*20\"}"; + file = JsonSerializer.Deserialize(json); + file.CreationTime.ShouldNotBeNull(); + + file.CreationTime.Value.Year.ShouldBe(2020); + file.CreationTime.Value.Month.ShouldBe(11); + file.CreationTime.Value.Day.ShouldBe(20); + } } } diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs index fbc222a46a..1e2c50a448 100644 --- a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs @@ -6,21 +6,28 @@ using Volo.Abp.Domain.Entities; namespace Volo.Abp.MemoryDb.JsonConverters { public class EntityJsonConverter : JsonConverter - where TEntity : IEntity + where TEntity : Entity { public override TEntity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var jsonDocument = JsonDocument.ParseValue(ref reader); - var entity = (TEntity)JsonSerializer.Deserialize(jsonDocument.RootElement.GetRawText(), typeToConvert); - var idJsonElement = jsonDocument.RootElement.GetProperty(nameof(IEntity.Id)); - - var id = JsonSerializer.Deserialize(idJsonElement.GetRawText()); - if (id != null) + if (jsonDocument.RootElement.ValueKind == JsonValueKind.Object) { - EntityHelper.TrySetId(entity, () => id); + var entity = (TEntity)JsonSerializer.Deserialize(jsonDocument.RootElement.GetRawText(), typeToConvert); + + var idJsonElement = jsonDocument.RootElement.GetProperty(nameof(Entity.Id)); + if (idJsonElement.ValueKind != JsonValueKind.Undefined) + { + var id = JsonSerializer.Deserialize(idJsonElement.GetRawText()); + if (id != null) + { + EntityHelper.TrySetId(entity, () => id); + } + } + return entity; } - return entity; + throw new JsonException("RootElement ValueKind is not Object!"); } public override void Write(Utf8JsonWriter writer, TEntity value, JsonSerializerOptions options) From 6404c02764647acc445dcec6395602d040e3cf70 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 14 Nov 2020 13:04:14 +0800 Subject: [PATCH 07/15] Fix unit test. --- ...pSystemTextJsonSerializerProvider_Tests.cs | 115 +++++++++--------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index 3c32a99dec..9e166aa146 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -8,15 +8,71 @@ using Xunit; namespace Volo.Abp.Json { - public class AbpSystemTextJsonSerializerProvider_Tests : AbpJsonTestBase + public abstract class AbpSystemTextJsonSerializerProvider_TestBase : AbpJsonTestBase { protected AbpSystemTextJsonSerializerProvider JsonSerializer; - public AbpSystemTextJsonSerializerProvider_Tests() + public AbpSystemTextJsonSerializerProvider_TestBase() { JsonSerializer = GetRequiredService(); } + public class TestExtensibleObjectClass : ExtensibleObject + { + public string Name { get; set; } + } + + public class FileWithBoolean + { + public string Name { get; set; } + + public bool IsDeleted { get; set; } + } + + public class FileWithNullableBoolean + { + public string Name { get; set; } + + public bool? IsDeleted { get; set; } + } + + public class FileWithEnum + { + public string Name { get; set; } + + public FileType Type { get; set; } + } + + public class FileWithNullableEnum + { + public string Name { get; set; } + + public FileType? Type { get; set; } + } + + public enum FileType + { + Zip = 0, + Exe = 2 + } + + public class FileWithDatetime + { + public string Name { get; set; } + + public DateTime CreationTime { get; set; } + } + + public class FileWithNullableDatetime + { + public string Name { get; set; } + + public DateTime? CreationTime { get; set; } + } + } + + public class AbpSystemTextJsonSerializerProvider_Tests : AbpSystemTextJsonSerializerProvider_TestBase + { [Fact] public void Serialize_Deserialize_With_Boolean() { @@ -132,62 +188,9 @@ namespace Volo.Abp.Json file.CreationTime.Value.Month.ShouldBe(11); file.CreationTime.Value.Day.ShouldBe(20); } - - class TestExtensibleObjectClass : ExtensibleObject - { - public string Name { get; set; } - } - - class FileWithBoolean - { - public string Name { get; set; } - - public bool IsDeleted { get; set; } - } - - class FileWithNullableBoolean - { - public string Name { get; set; } - - public bool? IsDeleted { get; set; } - } - - class FileWithEnum - { - public string Name { get; set; } - - public FileType Type { get; set; } - } - - class FileWithNullableEnum - { - public string Name { get; set; } - - public FileType? Type { get; set; } - } - - enum FileType - { - Zip = 0, - Exe = 2 - } - - protected class FileWithDatetime - { - public string Name { get; set; } - - public DateTime CreationTime { get; set; } - } - - protected class FileWithNullableDatetime - { - public string Name { get; set; } - - public DateTime? CreationTime { get; set; } - } } - public class FormatAbpSystemTextJsonSerializerProvider_Tests : AbpSystemTextJsonSerializerProvider_Tests + public class AbpSystemTextJsonSerializerProviderDefaultDateTimeFormat_Tests : AbpSystemTextJsonSerializerProvider_TestBase { protected override void AfterAddApplication(IServiceCollection services) { From 0768f34776bc91855e68508fadc3b56a8d348d7c Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 14 Nov 2020 13:49:37 +0800 Subject: [PATCH 08/15] Add more datetime unit tests. --- ...pSystemTextJsonSerializerProvider_Tests.cs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index 9e166aa146..06e858c7c0 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -4,6 +4,7 @@ using Shouldly; using Volo.Abp.Data; using Volo.Abp.Json.SystemTextJson; using Volo.Abp.ObjectExtending; +using Volo.Abp.Timing; using Xunit; namespace Volo.Abp.Json @@ -167,6 +168,9 @@ namespace Volo.Abp.Json file.CreationTime.Year.ShouldBe(2020); file.CreationTime.Month.ShouldBe(11); file.CreationTime.Day.ShouldBe(20); + + var newJson = JsonSerializer.Serialize(file); + newJson.ShouldBe(json); } [Fact] @@ -187,10 +191,13 @@ namespace Volo.Abp.Json file.CreationTime.Value.Year.ShouldBe(2020); file.CreationTime.Value.Month.ShouldBe(11); file.CreationTime.Value.Day.ShouldBe(20); + + var newJson = JsonSerializer.Serialize(file); + newJson.ShouldBe(json); } } - public class AbpSystemTextJsonSerializerProviderDefaultDateTimeFormat_Tests : AbpSystemTextJsonSerializerProvider_TestBase + public class AbpSystemTextJsonSerializerProvider_DateTimeFormat_Tests : AbpSystemTextJsonSerializerProvider_TestBase { protected override void AfterAddApplication(IServiceCollection services) { @@ -208,6 +215,9 @@ namespace Volo.Abp.Json file.CreationTime.Year.ShouldBe(2020); file.CreationTime.Month.ShouldBe(11); file.CreationTime.Day.ShouldBe(20); + + var newJson = JsonSerializer.Serialize(file); + newJson.ShouldBe(json); } [Fact] @@ -228,6 +238,45 @@ namespace Volo.Abp.Json file.CreationTime.Value.Year.ShouldBe(2020); file.CreationTime.Value.Month.ShouldBe(11); file.CreationTime.Value.Day.ShouldBe(20); + + var newJson = JsonSerializer.Serialize(file); + newJson.ShouldBe(json); } } + + public abstract class AbpSystemTextJsonSerializerProvider_Datetime_Kind_Tests : AbpSystemTextJsonSerializerProvider_TestBase + { + protected DateTimeKind Kind { get; set; } = DateTimeKind.Unspecified; + + [Fact] + public void Serialize_Deserialize() + { + var json = "{\"name\":\"abp\",\"creationTime\":\"2020-11-20T00:00:00\"}"; + var file = JsonSerializer.Deserialize(json); + file.CreationTime.Kind.ShouldBe(Kind); + } + } + + public class AbpSystemTextJsonSerializerProvider_Datetime_Kind_UTC_Tests : AbpSystemTextJsonSerializerProvider_Datetime_Kind_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Utc; + services.Configure(x => x.Kind = Kind); + } + } + + public class AbpSystemTextJsonSerializerProvider_Datetime_Kind_Local_Tests : AbpSystemTextJsonSerializerProvider_Datetime_Kind_Tests + { + protected override void AfterAddApplication(IServiceCollection services) + { + Kind = DateTimeKind.Local; + services.Configure(x => x.Kind = Kind); + } + } + + public class AbpSystemTextJsonSerializerProvider_Datetime_Kind_Unspecified_Tests : AbpSystemTextJsonSerializerProvider_Datetime_Kind_Tests + { + + } } From 9e05025ad856b8c244168956419f09bf04aab7b3 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 14 Nov 2020 14:42:58 +0800 Subject: [PATCH 09/15] Enhance JsonConverter in the Feature module. --- .../AbpHasExtraPropertiesJsonConverter.cs | 6 ++--- ...ctionStringValueItemSourceJsonConverter.cs | 21 +++++++++------ .../StringValueTypeJsonConverter.cs | 26 ++++++++++++------ .../ValueValidatorJsonConverter.cs | 27 ++++++++++++------- .../StringValueJsonConverter_Tests.cs | 11 ++++++++ 5 files changed, 63 insertions(+), 28 deletions(-) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs index e5e3f5e187..466fee45f3 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs @@ -20,10 +20,10 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters { var extensibleObject = JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions); - var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)); - if (extraProperties.Value.ValueKind == JsonValueKind.Object) + var extraPropertiesJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase)); + if (extraPropertiesJsonProperty.Value.ValueKind == JsonValueKind.Object) { - var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties.Value.GetRawText(), typeof(ExtraPropertyDictionary), newOptions); + var extraPropertyDictionary = JsonSerializer.Deserialize(extraPropertiesJsonProperty.Value.GetRawText(), typeof(ExtraPropertyDictionary), newOptions); ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary); } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs index 3a05e3f63f..bae37dbff9 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; @@ -11,16 +10,22 @@ namespace Volo.Abp.FeatureManagement.JsonConverters { public override ISelectionStringValueItemSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var newOptions = JsonSerializerOptionsHelper.Create(options, this); - var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var items = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(ISelectionStringValueItemSource.Items), StringComparison.OrdinalIgnoreCase)).Value.GetRawText(); + var itemsJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => + x.Name.Equals(nameof(ISelectionStringValueItemSource.Items), StringComparison.OrdinalIgnoreCase)); + + if (itemsJsonProperty.Value.ValueKind == JsonValueKind.Array) + { + var newOptions = JsonSerializerOptionsHelper.Create(options, this); + + var selectionStringValueItem = + JsonSerializer.Deserialize(itemsJsonProperty.Value.GetRawText(), newOptions) ?? + Array.Empty(); - var selectionStringValueItem = - JsonSerializer.Deserialize>(items, newOptions) ?? - new List(); + return new StaticSelectionStringValueItemSource(selectionStringValueItem.As()); + } - return new StaticSelectionStringValueItemSource(selectionStringValueItem.ToArray().As()); + throw new JsonException($"Can't to get the {nameof(ISelectionStringValueItemSource.Items)} property of {nameof(ISelectionStringValueItemSource)}!"); } public override void Write(Utf8JsonWriter writer, ISelectionStringValueItemSource value, JsonSerializerOptions options) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs index 08ba6326b9..0d2196e911 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs @@ -11,15 +11,25 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override IStringValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var name = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IStringValueType.Name), StringComparison.OrdinalIgnoreCase)).Value.GetString(); - var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), new SelectionStringValueItemSourceJsonConverter()); - return name switch + + var nameJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IStringValueType.Name), StringComparison.OrdinalIgnoreCase)); + if (nameJsonProperty.Value.ValueKind == JsonValueKind.String) { - "SelectionStringValueType" => JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions), - "FreeTextStringValueType" => JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions), - "ToggleStringValueType" => JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions), - _ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!") - }; + var name = nameJsonProperty.Value.GetString(); + + var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), + new SelectionStringValueItemSourceJsonConverter()); + + return name switch + { + "SelectionStringValueType" => JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions), + "FreeTextStringValueType" => JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions), + "ToggleStringValueType" => JsonSerializer.Deserialize(rootElement.GetRawText(), newOptions), + _ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!") + }; + } + + throw new JsonException($"Can't to get the {nameof(IStringValueType.Name)} property of {nameof(IStringValueType)}!"); } public override void Write(Utf8JsonWriter writer, IStringValueType value, JsonSerializerOptions options) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs index 98c1013c12..25bb603c1a 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs @@ -13,21 +13,30 @@ namespace Volo.Abp.FeatureManagement.JsonConverters public override IValueValidator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var rootElement = JsonDocument.ParseValue(ref reader).RootElement; - var name = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Name), StringComparison.OrdinalIgnoreCase)).Value.GetString(); - var valueValidator = CreateValueValidatorByName(name); - var propertiesRawText = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Properties), StringComparison.OrdinalIgnoreCase)).Value.GetRawText(); - var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter()); - var properties = JsonSerializer.Deserialize>(propertiesRawText, newOptions); - if (properties != null && properties.Any()) + var nameJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Name), StringComparison.OrdinalIgnoreCase)); + if (nameJsonProperty.Value.ValueKind == JsonValueKind.String) { - foreach (var property in properties) + var valueValidator = CreateValueValidatorByName(nameJsonProperty.Value.GetString()); + + var propertiesJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Properties), StringComparison.OrdinalIgnoreCase)); + if (propertiesJsonProperty.Value.ValueKind == JsonValueKind.Object) { - valueValidator.Properties[property.Key] = property.Value; + var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter()); + var properties = JsonSerializer.Deserialize>(propertiesJsonProperty.Value.GetRawText(), newOptions); + if (properties != null && properties.Any()) + { + foreach (var property in properties) + { + valueValidator.Properties[property.Key] = property.Value; + } + } } + + return valueValidator; } - return valueValidator; + throw new JsonException($"Can't to get the {nameof(IValueValidator.Name)} property of {nameof(IValueValidator)}!"); } public override void Write(Utf8JsonWriter writer, IValueValidator value, JsonSerializerOptions options) diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs index c72cbddffd..0a96027317 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs @@ -60,6 +60,14 @@ namespace Volo.Abp.FeatureManagement MinValue = 10 } } + }, + new FeatureDto + { + Provider = new FeatureProviderDto + { + Name = "FeatureName", + Key = "FeatureKey" + } } } } @@ -85,6 +93,9 @@ namespace Volo.Abp.FeatureManagement featureListDto2.Groups[0].Features[2].ValueType.Validator.ShouldBeOfType(); featureListDto2.Groups[0].Features[2].ValueType.Validator.As().MaxValue.ShouldBe(1000); featureListDto2.Groups[0].Features[2].ValueType.Validator.As().MinValue.ShouldBe(10); + + featureListDto2.Groups[0].Features[3].Provider.Name.ShouldBe("FeatureName"); + featureListDto2.Groups[0].Features[3].Provider.Key.ShouldBe("FeatureKey"); } } } From 9b8ab09f23d13be8ca948623d448056af48cab0c Mon Sep 17 00:00:00 2001 From: YinChang0626 Date: Sun, 15 Nov 2020 00:17:17 +0800 Subject: [PATCH 10/15] fix bug of FeatureManager.GetAllWithProviderAsync and add unittest --- .../Volo/Abp/FeatureManagement/FeatureManager.cs | 6 ++---- .../Abp/FeatureManagement/FeatureManager_Tests.cs | 11 ++++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs index 38c61cc743..93618e39c7 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs @@ -84,9 +84,7 @@ namespace Volo.Abp.FeatureManagement providers = providers.TakeWhile(c => c.Name == providerName); } - var providerList = providers.Reverse().ToList(); - - if (!providerList.Any()) + if (!providers.Any()) { return new List(); } @@ -96,7 +94,7 @@ namespace Volo.Abp.FeatureManagement foreach (var feature in featureDefinitions) { var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(feature.Name, null); - foreach (var provider in providerList) + foreach (var provider in providers) { string pk = null; if (provider.Compatible(providerName)) diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs index d86bcfaa48..fab724d604 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Features; @@ -19,7 +20,15 @@ namespace Volo.Abp.FeatureManagement _featureChecker = GetRequiredService(); _currentTenant = GetRequiredService(); } - + [Fact] + public async Task Should_Get_A_Correct_FeatureValue_By_GetAllWithProviderAsync() + { + (await _featureManager.GetAllWithProviderAsync( + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString() + )).FirstOrDefault(x=>x.Name== TestFeatureDefinitionProvider.ProjectCount) + .Value.ShouldBe("3"); + } [Fact] public async Task Should_Get_A_FeatureValue_For_A_Provider() { From 00f5169b867ed559555eeb406cb849f2f0dfbf3c Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 15 Nov 2020 14:32:55 +0800 Subject: [PATCH 11/15] Refactor. --- .../Abp/FeatureManagement/FeatureManager.cs | 10 ++-- .../FeatureManagement/FeatureManager_Tests.cs | 52 +++++++++++++++---- .../FeatureValueRepository_Tests.cs | 2 +- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs index 93618e39c7..0eb447e5c6 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -76,15 +76,15 @@ namespace Volo.Abp.FeatureManagement Check.NotNull(providerName, nameof(providerName)); var featureDefinitions = FeatureDefinitionManager.GetAll(); - var providers = Enumerable.Reverse(Providers) - .SkipWhile(c => c.Name != providerName); + var providers = Enumerable.Reverse(Providers).SkipWhile(c => c.Name != providerName); if (!fallback) { providers = providers.TakeWhile(c => c.Name == providerName); } - if (!providers.Any()) + var providersList = providers.ToList(); + if (!providersList.Any()) { return new List(); } @@ -94,7 +94,7 @@ namespace Volo.Abp.FeatureManagement foreach (var feature in featureDefinitions) { var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(feature.Name, null); - foreach (var provider in providers) + foreach (var provider in providersList) { string pk = null; if (provider.Compatible(providerName)) diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs index fab724d604..232673c233 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs @@ -20,15 +20,7 @@ namespace Volo.Abp.FeatureManagement _featureChecker = GetRequiredService(); _currentTenant = GetRequiredService(); } - [Fact] - public async Task Should_Get_A_Correct_FeatureValue_By_GetAllWithProviderAsync() - { - (await _featureManager.GetAllWithProviderAsync( - EditionFeatureValueProvider.ProviderName, - TestEditionIds.Enterprise.ToString() - )).FirstOrDefault(x=>x.Name== TestFeatureDefinitionProvider.ProjectCount) - .Value.ShouldBe("3"); - } + [Fact] public async Task Should_Get_A_FeatureValue_For_A_Provider() { @@ -120,5 +112,47 @@ namespace Volo.Abp.FeatureManagement (await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeTrue(); } } + + + [Fact] + public async Task Should_Get_FeatureValues_With_Provider_For_A_Provider() + { + var featureNameValueWithGrantedProviders = await _featureManager.GetAllWithProviderAsync( + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString() + ); + + featureNameValueWithGrantedProviders.ShouldContain(x => + x.Name == TestFeatureDefinitionProvider.SocialLogins + && x.Value == true.ToString().ToLowerInvariant() && + x.Provider.Name == EditionFeatureValueProvider.ProviderName); + + featureNameValueWithGrantedProviders.ShouldContain(x => + x.Name == TestFeatureDefinitionProvider.EmailSupport && + x.Value == true.ToString().ToLowerInvariant() && + x.Provider.Name == EditionFeatureValueProvider.ProviderName); + + //Default Value + featureNameValueWithGrantedProviders.ShouldContain(x => + x.Name == TestFeatureDefinitionProvider.DailyAnalysis && + x.Value == false.ToString().ToLowerInvariant() && + x.Provider.Name == DefaultValueFeatureValueProvider.ProviderName); + + featureNameValueWithGrantedProviders.ShouldContain(x => + x.Name == TestFeatureDefinitionProvider.UserCount && + x.Value == "20" && + x.Provider.Name == EditionFeatureValueProvider.ProviderName); + + featureNameValueWithGrantedProviders.ShouldContain(x => + x.Name == TestFeatureDefinitionProvider.ProjectCount && + x.Value == "3" && + x.Provider.Name == EditionFeatureValueProvider.ProviderName); + + featureNameValueWithGrantedProviders.ShouldContain(x => + x.Name == TestFeatureDefinitionProvider.BackupCount && + x.Value == "5" && + x.Provider.Name == EditionFeatureValueProvider.ProviderName); + } + } } diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs index db606b2582..6cc50f7378 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs @@ -41,7 +41,7 @@ namespace Volo.Abp.FeatureManagement } [Fact] - public async Task FindAAllsync() + public async Task FindAllAsync() { var featureValues = await Repository.FindAllAsync( TestFeatureDefinitionProvider.ProjectCount, From 3372be5daa80b97ff31517d3b536136e0321d336 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 15 Nov 2020 14:35:16 +0800 Subject: [PATCH 12/15] Rename providersList to providerList. --- .../Volo/Abp/FeatureManagement/FeatureManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs index 0eb447e5c6..6d7c6d59c2 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs @@ -83,8 +83,8 @@ namespace Volo.Abp.FeatureManagement providers = providers.TakeWhile(c => c.Name == providerName); } - var providersList = providers.ToList(); - if (!providersList.Any()) + var providerList = providers.ToList(); + if (!providerList.Any()) { return new List(); } @@ -94,7 +94,7 @@ namespace Volo.Abp.FeatureManagement foreach (var feature in featureDefinitions) { var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(feature.Name, null); - foreach (var provider in providersList) + foreach (var provider in providerList) { string pk = null; if (provider.Compatible(providerName)) From 71bfe2af8d1d051e42f676c43248210616d8813a Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 15 Nov 2020 17:39:22 +0800 Subject: [PATCH 13/15] Use CultureInfo.CurrentUICulture of DateTime JSON converter. --- .../JsonConverters/AbpDateTimeConverter.cs | 10 +++++----- .../AbpHasExtraPropertiesJsonConverter.cs | 2 +- .../AbpNullableDateTimeConverter.cs | 8 ++++---- ...pSystemTextJsonSerializerProvider_Tests.cs | 20 ++++++++++++++++++- .../JsonConverters/EntityJsonConverter.cs | 2 +- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs index e35589830d..0d571aa0e8 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpDateTimeConverter.cs @@ -26,15 +26,15 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters if (reader.TokenType == JsonTokenType.String) { var s = reader.GetString(); - if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var d1)) + if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1)) { return _clock.Normalize(d1); } - throw new JsonException($"{s} cannot parse to DateTime({_options.DefaultDateTimeFormat})!"); + throw new JsonException($"'{s}' can't parse to DateTime({_options.DefaultDateTimeFormat})!"); } - throw new JsonException("reader TokenType is not String!"); + throw new JsonException("Reader's TokenType is not String!"); } if (reader.TryGetDateTime(out var d2)) @@ -42,7 +42,7 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters return _clock.Normalize(d2); } - throw new JsonException("reader can't get datetime!"); + throw new JsonException("Can't get datetime from the reader!"); } public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) @@ -53,7 +53,7 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters } else { - writer.WriteStringValue(_clock.Normalize(value).ToString(_options.DefaultDateTimeFormat)); + writer.WriteStringValue(_clock.Normalize(value).ToString(_options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture)); } } } diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs index 466fee45f3..47640ec306 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpHasExtraPropertiesJsonConverter.cs @@ -30,7 +30,7 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters return extensibleObject; } - throw new JsonException("RootElement ValueKind is not Object!"); + throw new JsonException("RootElement's ValueKind is not Object!"); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) diff --git a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs index 3a603af994..ad9e6cb52a 100644 --- a/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs +++ b/framework/src/Volo.Abp.Json/Volo/Abp/Json/SystemTextJson/JsonConverters/AbpNullableDateTimeConverter.cs @@ -26,15 +26,15 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters if (reader.TokenType == JsonTokenType.String) { var s = reader.GetString(); - if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var d1)) + if (DateTime.TryParseExact(s, _options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, out var d1)) { return _clock.Normalize(d1); } - throw new JsonException($"{s} cannot parse to DateTime({_options.DefaultDateTimeFormat})!"); + throw new JsonException($"'{s}' can't parse to DateTime({_options.DefaultDateTimeFormat})!"); } - throw new JsonException("reader TokenType is not String!"); + throw new JsonException("Reader's TokenType is not String!"); } if (reader.TryGetDateTime(out var d2)) @@ -59,7 +59,7 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters } else { - writer.WriteStringValue(_clock.Normalize(value.Value).ToString(_options.DefaultDateTimeFormat)); + writer.WriteStringValue(_clock.Normalize(value.Value).ToString(_options.DefaultDateTimeFormat, CultureInfo.CurrentUICulture)); } } } diff --git a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs index 06e858c7c0..3f7ce5dc26 100644 --- a/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs +++ b/framework/test/Volo.Abp.Json.Tests/Volo/Abp/Json/AbpSystemTextJsonSerializerProvider_Tests.cs @@ -152,7 +152,7 @@ namespace Volo.Abp.Json } [Fact] - public void Serialize_Deserialize_ExtensibleObject_With_ExtraProperties_String() + public void Serialize_Deserialize_ExtensibleObject_Without_String() { var json = "{\"name\":\"test\"}"; var extensibleObject = JsonSerializer.Deserialize(json); @@ -160,6 +160,24 @@ namespace Volo.Abp.Json extensibleObject.ExtraProperties.ShouldBeEmpty(); } + [Fact] + public void Serialize_Deserialize_ExtensibleObject_Without_Empty() + { + var json = "{\"name\":\"test\",\"extraProperties\":{}}"; + var extensibleObject = JsonSerializer.Deserialize(json); + extensibleObject.ExtraProperties.ShouldNotBeNull(); + extensibleObject.ExtraProperties.ShouldBeEmpty(); + } + + [Fact] + public void Serialize_Deserialize_ExtensibleObject_Without_Null() + { + var json = "{\"name\":\"test\",\"extraProperties\":null}"; + var extensibleObject = JsonSerializer.Deserialize(json); + extensibleObject.ExtraProperties.ShouldNotBeNull(); + extensibleObject.ExtraProperties.ShouldBeEmpty(); + } + [Fact] public void Serialize_Deserialize_With_Datetime() { diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs index 1e2c50a448..6d21074e92 100644 --- a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/JsonConverters/EntityJsonConverter.cs @@ -27,7 +27,7 @@ namespace Volo.Abp.MemoryDb.JsonConverters return entity; } - throw new JsonException("RootElement ValueKind is not Object!"); + throw new JsonException("RootElement's ValueKind is not Object!"); } public override void Write(Utf8JsonWriter writer, TEntity value, JsonSerializerOptions options) From 600dfb55331339d9a37be9f61eed48f74a405f37 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 16 Nov 2020 10:06:26 +0800 Subject: [PATCH 14/15] Add OrderBy top the First/FirstOrDefault operator. Resolve #6179 --- .../ApiResources/ApiResourceRepository.cs | 1 + .../IdentityServer/ApiScopes/ApiScopeRepository.cs | 1 + .../Devices/DeviceFlowCodesRepository.cs | 11 +++++++---- .../Grants/PersistedGrantRepository.cs | 5 ++++- .../IdentityResources/IdentityResourceRepository.cs | 1 + .../MongoDB/MongoApiResourceRepository.cs | 1 + .../MongoDB/MongoApiScopeRepository.cs | 6 +++++- .../IdentityServer/MongoDB/MongoClientRepository.cs | 5 ++++- .../MongoDB/MongoDeviceFlowCodesRepository.cs | 13 ++++++++----- .../MongoDB/MongoIdentityResourceRepository.cs | 1 + .../MongoDB/MongoPersistedGrantRepository.cs | 7 ++++--- 11 files changed, 37 insertions(+), 15 deletions(-) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs index bdfe2bbb81..3cdcb7e49d 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs @@ -22,6 +22,7 @@ namespace Volo.Abp.IdentityServer.ApiResources { var query = from apiResource in DbSet.IncludeDetails(includeDetails) where apiResource.Name == apiResourceName + orderby apiResource.Id select apiResource; return await query.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiScopes/ApiScopeRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiScopes/ApiScopeRepository.cs index 8e5d2bd09c..031cc55b14 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiScopes/ApiScopeRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiScopes/ApiScopeRepository.cs @@ -28,6 +28,7 @@ namespace Volo.Abp.IdentityServer.ApiScopes { var query = from scope in DbSet.IncludeDetails(includeDetails) where scopeNames.Contains(scope.Name) + orderby scope.Id select scope; return await query.ToListAsync(GetCancellationToken(cancellationToken)); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesRepository.cs index f688300a55..819c90d402 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Devices/DeviceFlowCodesRepository.cs @@ -24,16 +24,19 @@ namespace Volo.Abp.IdentityServer.Devices CancellationToken cancellationToken = default) { return await DbSet - .FirstOrDefaultAsync(d => d.UserCode == userCode, GetCancellationToken(cancellationToken)) - ; + .Where(d => d.UserCode == userCode) + .OrderBy(d => d.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task FindByDeviceCodeAsync( - string deviceCode, + string deviceCode, CancellationToken cancellationToken = default) { return await DbSet - .FirstOrDefaultAsync(d => d.DeviceCode == deviceCode, GetCancellationToken(cancellationToken)); + .Where(d => d.DeviceCode == deviceCode) + .OrderBy(d => d.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task> GetListByExpirationAsync(DateTime maxExpirationDate, int maxResultCount, diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Grants/PersistedGrantRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Grants/PersistedGrantRepository.cs index 9b54a4cf24..aa7a27804f 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Grants/PersistedGrantRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Grants/PersistedGrantRepository.cs @@ -29,7 +29,10 @@ namespace Volo.Abp.IdentityServer.Grants string key, CancellationToken cancellationToken = default) { - return await DbSet.FirstOrDefaultAsync(x => x.Key == key, GetCancellationToken(cancellationToken)); + return await DbSet + .Where(x => x.Key == key) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task> GetListBySubjectIdAsync( diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs index e86732c1e5..6ddf38e2db 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs @@ -57,6 +57,7 @@ namespace Volo.Abp.IdentityServer.IdentityResources return await DbSet .IncludeDetails(includeDetails) .Where(x => x.Name == name) + .OrderBy(x => x.Id) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs index 0f4337b895..599fa913b5 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs @@ -23,6 +23,7 @@ namespace Volo.Abp.IdentityServer.MongoDB { return await GetMongoQueryable() .Where(ar => ar.Name == apiResourceName) + .OrderBy(ar => ar.Id) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiScopeRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiScopeRepository.cs index a446e772e8..91a408392f 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiScopeRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiScopeRepository.cs @@ -22,7 +22,10 @@ namespace Volo.Abp.IdentityServer.MongoDB public async Task GetByNameAsync(string scopeName, bool includeDetails = true, CancellationToken cancellationToken = default) { - return await GetMongoQueryable().FirstOrDefaultAsync(x => x.Name == scopeName, GetCancellationToken(cancellationToken)); + return await GetMongoQueryable() + .Where(x => x.Name == scopeName) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public async Task> GetListByNameAsync(string[] scopeNames, bool includeDetails = false, @@ -30,6 +33,7 @@ namespace Volo.Abp.IdentityServer.MongoDB { var query = from scope in GetMongoQueryable() where scopeNames.Contains(scope.Name) + orderby scope.Id select scope; return await query.ToListAsync(GetCancellationToken(cancellationToken)); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs index 3f747e963d..a68f5738d0 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs @@ -26,7 +26,10 @@ namespace Volo.Abp.IdentityServer.MongoDB bool includeDetails = true, CancellationToken cancellationToken = default) { - return await GetMongoQueryable().FirstOrDefaultAsync(x => x.ClientId == clientId, GetCancellationToken(cancellationToken)); + return await GetMongoQueryable() + .Where(x => x.ClientId == clientId) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task> GetListAsync( diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoDeviceFlowCodesRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoDeviceFlowCodesRepository.cs index be0d964287..fc6dc0100a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoDeviceFlowCodesRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoDeviceFlowCodesRepository.cs @@ -24,18 +24,21 @@ namespace Volo.Abp.IdentityServer.MongoDB CancellationToken cancellationToken = default) { return await GetMongoQueryable() - .FirstOrDefaultAsync(d => d.UserCode == userCode, GetCancellationToken(cancellationToken)) - ; + .Where(d => d.UserCode == userCode) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task FindByDeviceCodeAsync(string deviceCode, CancellationToken cancellationToken = default) { return await GetMongoQueryable() - .FirstOrDefaultAsync(d => d.DeviceCode == deviceCode, GetCancellationToken(cancellationToken)); + .Where(d => d.DeviceCode == deviceCode) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task> GetListByExpirationAsync( - DateTime maxExpirationDate, + DateTime maxExpirationDate, int maxResultCount, CancellationToken cancellationToken = default) { @@ -46,4 +49,4 @@ namespace Volo.Abp.IdentityServer.MongoDB .ToListAsync(GetCancellationToken(cancellationToken)); } } -} \ No newline at end of file +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs index 533136ebb3..15fdf5b00c 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs @@ -37,6 +37,7 @@ namespace Volo.Abp.IdentityServer.MongoDB { return await GetMongoQueryable() .Where(x => x.Name == name) + .OrderBy(x => x.Id) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs index 6af1f5488d..6265bcd019 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -27,9 +27,10 @@ namespace Volo.Abp.IdentityServer.MongoDB public virtual async Task FindByKeyAsync(string key, CancellationToken cancellationToken = default) { - return await GetMongoQueryable() - .FirstOrDefaultAsync(x => x.Key == key, GetCancellationToken(cancellationToken)); + .Where(x => x.Key == key) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task> GetListBySubjectIdAsync(string subjectId, CancellationToken cancellationToken = default) From 936ddba74d417074dee2e7052106ba463cd1bfe6 Mon Sep 17 00:00:00 2001 From: Erol Arkat Date: Mon, 16 Nov 2020 12:54:00 +0300 Subject: [PATCH 15/15] version update --- common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.props b/common.props index b613955758..0bc2e47e4a 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 4.0.0-rc.2 + 4.0.0-rc.3 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/