@ -0,0 +1,38 @@ |
|||||
|
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app |
||||
|
|
||||
|
# Number of days of inactivity before a closed issue or pull request is locked |
||||
|
daysUntilLock: 30 |
||||
|
|
||||
|
# Skip issues and pull requests created before a given timestamp. Timestamp must |
||||
|
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable |
||||
|
skipCreatedBefore: false |
||||
|
|
||||
|
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable |
||||
|
exemptLabels: [] |
||||
|
|
||||
|
# Label to add before locking, such as `outdated`. Set to `false` to disable |
||||
|
lockLabel: false |
||||
|
|
||||
|
# Comment to post before locking. Set to `false` to disable |
||||
|
lockComment: > |
||||
|
This thread has been automatically locked since there has not been |
||||
|
any recent activity after it was closed. Please open a new issue for |
||||
|
related bugs. |
||||
|
|
||||
|
# Assign `resolved` as the reason for locking. Set to `false` to disable |
||||
|
setLockReason: true |
||||
|
|
||||
|
# Limit to only `issues` or `pulls` |
||||
|
# only: issues |
||||
|
|
||||
|
# Optionally, specify configuration settings just for `issues` or `pulls` |
||||
|
# issues: |
||||
|
# exemptLabels: |
||||
|
# - help-wanted |
||||
|
# lockLabel: outdated |
||||
|
|
||||
|
# pulls: |
||||
|
# daysUntilLock: 30 |
||||
|
|
||||
|
# Repository to extend settings from |
||||
|
# _extends: repo |
||||
@ -0,0 +1,15 @@ |
|||||
|
# Number of days of inactivity before an issue becomes stale |
||||
|
daysUntilStale: 60 |
||||
|
# Number of days of inactivity before a stale issue is closed |
||||
|
daysUntilClose: 7 |
||||
|
# Set to true to ignore issues in a milestone (defaults to false) |
||||
|
exemptMilestones: true |
||||
|
# Label to use when marking an issue as stale |
||||
|
staleLabel: inactive |
||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable |
||||
|
markComment: > |
||||
|
This issue has been automatically marked as stale because it has not had |
||||
|
recent activity. It will be closed if no further activity occurs. Thank you |
||||
|
for your contributions. |
||||
|
# Comment to post when closing a stale issue. Set to `false` to disable |
||||
|
closeComment: false |
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,205 @@ |
|||||
|
# 轻松取消将你的观察取消订阅 |
||||
|
|
||||
|
`SubscriptionService` 是一个实用工具服务,它提供了一个简单的取消订阅Angular组件和指令中的RxJS可观察对象的功能. 请参见[为什么在实例销毁时要取消订阅可观察对象](https://angular.io/guide/lifecycle-hooks#cleaning-up-on-instance-destruction). |
||||
|
|
||||
|
## 入门 |
||||
|
|
||||
|
你必须在组件或指令级别提供 `SubscriptionService`,因为它没有在**根中提供**,而且它与组件/指令的生命周期同步. 只有在此之后,您才能注入并开始使用它。 |
||||
|
|
||||
|
```js |
||||
|
import { SubscriptionService } from '@abp/ng.core'; |
||||
|
|
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent { |
||||
|
count$ = interval(1000); |
||||
|
|
||||
|
constructor(private subscription: SubscriptionService) { |
||||
|
this.subscription.addOne(this.count$, console.log); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
由 `count$` 发出的值将被记录下来,直到组件被销毁. 你不必手动退订. |
||||
|
|
||||
|
> 请不要尝试使用单例 `SubscriptionService`. 这是行不通的. |
||||
|
|
||||
|
## 用法 |
||||
|
|
||||
|
## 如何订阅可观察对象 |
||||
|
|
||||
|
你可以传递 `next` 函数和 `error` 函数. |
||||
|
|
||||
|
|
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
const source$ = interval(1000); |
||||
|
const nextFn = value => console.log(value * 2); |
||||
|
const errorFn = error => { |
||||
|
console.error(error); |
||||
|
return of(null); |
||||
|
}; |
||||
|
|
||||
|
this.subscription.addOne(source$, nextFn, errorFn); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
或者,你可以传递一个观察者. |
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
const source$ = interval(1000); |
||||
|
const observer = { |
||||
|
next: value => console.log(value * 2), |
||||
|
complete: () => console.log('DONE'), |
||||
|
}; |
||||
|
|
||||
|
this.subscription.addOne(source$, observer); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
`addOne` 方法返回单个订阅以便你以后使用它. 有关详细信息,请参见下面的主题. |
||||
|
|
||||
|
### 实例销毁之前如何退订 |
||||
|
|
||||
|
有两种方法可以做到这一点. 如果你不想再次订阅.可以使用 `closeAll` 方法. |
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.subscription.addOne(interval(1000), console.log); |
||||
|
} |
||||
|
|
||||
|
onSomeEvent() { |
||||
|
this.subscription.closeAll(); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
这将清除所有订阅,你将无法再次订阅. 如果你打算添加另一个订阅,可以使用`reset`方法. |
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.subscription.addOne(interval(1000), console.log); |
||||
|
} |
||||
|
|
||||
|
onSomeEvent() { |
||||
|
this.subscription.reset(); |
||||
|
this.subscription.addOne(interval(1000), console.warn); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 如何取消单个订阅 |
||||
|
|
||||
|
有时你可能需要取消订阅特定的订阅,但保留其他订阅. 在这种情况下,你可以使用 `closeOne` 方法. |
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
countSubscription: Subscription; |
||||
|
|
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.countSubscription = this.subscription.addOne( |
||||
|
interval(1000), |
||||
|
console.log |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
onSomeEvent() { |
||||
|
this.subscription.closeOne(this.countSubscription); |
||||
|
console.log(this.countSubscription.closed); // true |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 如何从跟踪的订阅中删除单个订阅 |
||||
|
|
||||
|
你可能需要控制特定的订阅. 在这种情况下你可以使用 `removeOne` 方法将其从跟踪的订阅中删除. |
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
countSubscription: Subscription; |
||||
|
|
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.countSubscription = this.subscription.addOne( |
||||
|
interval(1000), |
||||
|
console.log |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
onSomeEvent() { |
||||
|
this.subscription.removeOne(this.countSubscription); |
||||
|
console.log(this.countSubscription.closed); // false |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 如何检查是否全部取消订阅 |
||||
|
|
||||
|
使用 `isClosed` 检查 `closeAll` 是否被调用. |
||||
|
|
||||
|
```js |
||||
|
@Component({ |
||||
|
/* class metadata here */ |
||||
|
providers: [SubscriptionService], |
||||
|
}) |
||||
|
class DemoComponent implements OnInit { |
||||
|
constructor(private subscription: SubscriptionService) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.subscription.addOne(interval(1000), console.log); |
||||
|
} |
||||
|
|
||||
|
onSomeEvent() { |
||||
|
console.log(this.subscription.isClosed); // false |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 下一步是什么? |
||||
|
|
||||
|
- [ListService](./List-Service.md) |
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,3 @@ |
|||||
|
# Dynamic JavaScript HTTP API Proxies |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,15 @@ |
|||||
|
using Newtonsoft.Json; |
||||
|
using Volo.Abp.Collections; |
||||
|
|
||||
|
namespace Volo.Abp.Json.Newtonsoft |
||||
|
{ |
||||
|
public class AbpNewtonsoftJsonSerializerOptions |
||||
|
{ |
||||
|
public ITypeList<JsonConverter> Converters { get; } |
||||
|
|
||||
|
public AbpNewtonsoftJsonSerializerOptions() |
||||
|
{ |
||||
|
Converters = new TypeList<JsonConverter>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "ar", |
||||
|
"texts": { |
||||
|
"DisplayName:Abp.Timing.Timezone": "المنطقة زمنية", |
||||
|
"Description:Abp.Timing.Timezone": "المنطقة الزمنية للتطبيق" |
||||
|
} |
||||
|
} |
||||
@ -1,34 +1,34 @@ |
|||||
{ |
{ |
||||
"culture": "ar", |
"culture": "ar", |
||||
"texts": { |
"texts": { |
||||
"'{0}' and '{1}' do not match.": "'{0}' and '{1}' do not match.", |
"'{0}' and '{1}' do not match.": "'{0}' و '{1}' غير متطابقان", |
||||
"The {0} field is not a valid credit card number.": "The {0} field is not a valid credit card number.", |
"The {0} field is not a valid credit card number.": "الحقل {0} لا يمثل رقم بطاقة ائتمان صالح.", |
||||
"{0} is not valid.": "{0} is not valid.", |
"{0} is not valid.": "{0} غير صالح.", |
||||
"The {0} field is not a valid e-mail address.": "The {0} field is not a valid e-mail address.", |
"The {0} field is not a valid e-mail address.": "الحقل {0} لا يمثل عنوان بريد إلكتروني صالح.", |
||||
"The {0} field only accepts files with the following extensions: {1}": "The {0} field only accepts files with the following extensions: {1}", |
"The {0} field only accepts files with the following extensions: {1}": "الحقل {0} يقبل الملفات ذات الامتدادات التالية فقط: {1}", |
||||
"The field {0} must be a string or array type with a maximum length of '{1}'.": "The field {0} must be a string or array type with a maximum length of '{1}'.", |
"The field {0} must be a string or array type with a maximum length of '{1}'.": "يجب أن يكون الحقل {0} عبارة عن سلسلة أو صفيف طوله '{1}' كحد أقصى.", |
||||
"The field {0} must be a string or array type with a minimum length of '{1}'.": "The field {0} must be a string or array type with a minimum length of '{1}'.", |
"The field {0} must be a string or array type with a minimum length of '{1}'.": "يجب أن يكون الحقل {0} عبارة عن سلسلة أو صفيف طوله كحد أدنى '{1}'.", |
||||
"The {0} field is not a valid phone number.": "The {0} field is not a valid phone number.", |
"The {0} field is not a valid phone number.": "الحقل {0} لا يمثل رقم هاتف صالح.", |
||||
"The field {0} must be between {1} and {2}.": "The field {0} must be between {1} and {2}.", |
"The field {0} must be between {1} and {2}.": "يجب أن يكون الحقل {0} بين {1} و {2}.", |
||||
"The field {0} must match the regular expression '{1}'.": "The field {0} must match the regular expression '{1}'.", |
"The field {0} must match the regular expression '{1}'.": "يجب أن يتطابق الحقل {0} مع التعبير النمطي '{1}'.", |
||||
"The {0} field is required.": "The {0} field is required.", |
"The {0} field is required.": "الحقل {0} إجباري.", |
||||
"The field {0} must be a string with a maximum length of {1}.": "The field {0} must be a string with a maximum length of {1}.", |
"The field {0} must be a string with a maximum length of {1}.": "يجب أن يكون الحقل {0} سلسلة أحرف طولها كحد أقصى {1}.", |
||||
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.", |
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "يجب أن يكون الحقل {0} سلسلة أحرف طولها {1} كحد أدنى و {2} كحد أقصى.", |
||||
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "The {0} field is not a valid fully-qualified http, https, or ftp URL.", |
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "الحقل {0} ليس عنوانا URL صالحًا مؤهلاً بالكامل سواء كان عنوان http أو https أو ftp", |
||||
"The field {0} is invalid.": "The field {0} is invalid.", |
"The field {0} is invalid.": "الحقل {0} غير صالح.", |
||||
"ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.", |
"ThisFieldIsNotAValidCreditCardNumber.": "هذا الحقل لا يمثل رقم بطاقة ائتمان صالح.", |
||||
"ThisFieldIsNotValid.": "This field is not valid.", |
"ThisFieldIsNotValid.": "هذا الحقل غير صالح.", |
||||
"ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.", |
"ThisFieldIsNotAValidEmailAddress.": "هذا الحقل لا يمثل عنوان بريد إلكتروني صالح.", |
||||
"ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "This field only accepts files with the following extensions: {0}", |
"ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "هذا الحقل يقبل الملفات ذات الامتدادات التالية فقط: {0}", |
||||
"ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthoOf{0}": "This field must be a string or array type with a maximum length of '{0}'.", |
"ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthOf{0}": "هذا الحقل يجب أن يكون سلسلة أحرف أو صفيف لا يتعدى طوله '{0}'.", |
||||
"ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "This field must be a string or array type with a minimum length of '{0}'.", |
"ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": " هذا الحقل يجب أن يكون عبارة عن سلسلة أو صفيف طوله كحد أدنى '{0}'.", |
||||
"ThisFieldIsNotAValidPhoneNumber.": "This field is not a valid phone number.", |
"ThisFieldIsNotAValidPhoneNumber.": "هذا الحقل لا يمثل رقم هاتف صالح.", |
||||
"ThisFieldMustBeBetween{0}And{1}": "This field must be between {0} and {1}.", |
"ThisFieldMustBeBetween{0}And{1}": "هذا الحقل يجب أن يكون بين {0} و {1}.", |
||||
"ThisFieldMustMatchTheRegularExpression{0}": "This field must match the regular expression '{0}'.", |
"ThisFieldMustMatchTheRegularExpression{0}": "هذا الحقل يجب أن يتطابق مع التعبير النمطي '{0}'.", |
||||
"ThisFieldIsRequired.": "This field is required.", |
"ThisFieldIsRequired.": "هذا الحقل إجباري.", |
||||
"ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "This field must be a string with a maximum length of {0}.", |
"ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "هذا الحقل يجب أن يكون سلسلة أحرف طولها كحد أقصى {0}.", |
||||
"ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "This field must be a string with a minimum length of {1} and a maximum length of {0}.", |
"ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "هذا الحقل يجب أن يكون سلسلة أحرف طولها {0} كحد أدنى و {1} كحد أقصى.", |
||||
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "This field is not a valid fully-qualified http, https, or ftp URL.", |
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "هذا الحقل ليس عنوانا URL صالحًا مؤهلاً بالكامل سواء كان عنوان http أو https أو ftp", |
||||
"ThisFieldIsInvalid.": "This field is invalid." |
"ThisFieldIsInvalid.": "هذا الحقل غير صالح." |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "ar", |
||||
|
"texts": { |
||||
|
"HelloText": "مرحبا {0}", |
||||
|
"HowAreYou": "كيف حالكم ؟" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,90 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using Newtonsoft.Json; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Validation.StringValues; |
||||
|
|
||||
|
namespace Volo.Abp.FeatureManagement |
||||
|
{ |
||||
|
public class StringValueTypeJsonConverter : JsonConverter, ITransientDependency |
||||
|
{ |
||||
|
public override bool CanWrite => false; |
||||
|
|
||||
|
public override bool CanConvert(Type objectType) |
||||
|
{ |
||||
|
return objectType == typeof(IStringValueType); |
||||
|
} |
||||
|
|
||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) |
||||
|
{ |
||||
|
throw new NotImplementedException("This method should not be called to write (since CanWrite is false)."); |
||||
|
} |
||||
|
|
||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) |
||||
|
{ |
||||
|
if (reader.TokenType != JsonToken.StartObject) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var jsonObject = JObject.Load(reader); |
||||
|
|
||||
|
var stringValue = CreateStringValueTypeByName(jsonObject, jsonObject["name"].ToString()); |
||||
|
foreach (var o in serializer.Deserialize<Dictionary<string, object>>( |
||||
|
new JsonTextReader(new StringReader(jsonObject["properties"].ToString())))) |
||||
|
{ |
||||
|
stringValue[o.Key] = o.Value; |
||||
|
} |
||||
|
|
||||
|
stringValue.Validator = CreateValueValidatorByName(jsonObject["validator"], jsonObject["validator"]["name"].ToString()); |
||||
|
foreach (var o in serializer.Deserialize<Dictionary<string, object>>( |
||||
|
new JsonTextReader(new StringReader(jsonObject["validator"]["properties"].ToString())))) |
||||
|
{ |
||||
|
stringValue.Validator[o.Key] = o.Value; |
||||
|
} |
||||
|
|
||||
|
return stringValue; |
||||
|
} |
||||
|
|
||||
|
protected virtual IStringValueType CreateStringValueTypeByName(JObject jObject, string name) |
||||
|
{ |
||||
|
if (name == "SelectionStringValueType") |
||||
|
{ |
||||
|
var selectionStringValueType = new SelectionStringValueType(); |
||||
|
if (jObject["itemSource"].HasValues) |
||||
|
{ |
||||
|
selectionStringValueType.ItemSource = new StaticSelectionStringValueItemSource(jObject["itemSource"]["items"] |
||||
|
.Select(item => new LocalizableSelectionStringValueItem() |
||||
|
{ |
||||
|
Value = item["value"].ToString(), |
||||
|
DisplayText = new LocalizableStringInfo(item["displayText"]["resourceName"].ToString(), item["displayText"]["name"].ToString()) |
||||
|
}).ToArray()); |
||||
|
} |
||||
|
|
||||
|
return selectionStringValueType; |
||||
|
} |
||||
|
|
||||
|
return name switch |
||||
|
{ |
||||
|
"FreeTextStringValueType" => new FreeTextStringValueType(), |
||||
|
"ToggleStringValueType" => new ToggleStringValueType(), |
||||
|
_ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!") |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected virtual IValueValidator CreateValueValidatorByName(JToken jObject, string name) |
||||
|
{ |
||||
|
return name switch |
||||
|
{ |
||||
|
"NULL" => new AlwaysValidValueValidator(), |
||||
|
"BOOLEAN" => new BooleanValueValidator(), |
||||
|
"NUMERIC" => new NumericValueValidator(), |
||||
|
"STRING" => new StringValueValidator(), |
||||
|
_ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!") |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Newtonsoft.Json; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.Validation.StringValues; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.FeatureManagement |
||||
|
{ |
||||
|
public class StringValueJsonConverter_Tests : FeatureManagementApplicationTestBase |
||||
|
{ |
||||
|
private readonly IJsonSerializer _jsonSerializer; |
||||
|
|
||||
|
public StringValueJsonConverter_Tests() |
||||
|
{ |
||||
|
_jsonSerializer = GetRequiredService<IJsonSerializer>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Serialize_And_Deserialize() |
||||
|
{ |
||||
|
var featureListDto = new FeatureListDto |
||||
|
{ |
||||
|
Features = new List<FeatureDto> |
||||
|
{ |
||||
|
new FeatureDto |
||||
|
{ |
||||
|
ValueType = new FreeTextStringValueType |
||||
|
{ |
||||
|
Validator = new BooleanValueValidator() |
||||
|
} |
||||
|
}, |
||||
|
new FeatureDto |
||||
|
{ |
||||
|
ValueType = new SelectionStringValueType |
||||
|
{ |
||||
|
ItemSource = new StaticSelectionStringValueItemSource( |
||||
|
new LocalizableSelectionStringValueItem |
||||
|
{ |
||||
|
Value = "TestValue", |
||||
|
DisplayText = new LocalizableStringInfo("TestResourceName", "TestName") |
||||
|
}), |
||||
|
Validator = new AlwaysValidValueValidator() |
||||
|
} |
||||
|
}, |
||||
|
new FeatureDto |
||||
|
{ |
||||
|
ValueType = new ToggleStringValueType |
||||
|
{ |
||||
|
Validator = new NumericValueValidator |
||||
|
{ |
||||
|
MaxValue = 1000, |
||||
|
MinValue = 10 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var serialized = _jsonSerializer.Serialize(featureListDto, indented: true); |
||||
|
var featureListDto2 = _jsonSerializer.Deserialize<FeatureListDto>(serialized); |
||||
|
|
||||
|
featureListDto2.Features[0].ValueType.ShouldBeOfType<FreeTextStringValueType>(); |
||||
|
featureListDto2.Features[0].ValueType.Validator.ShouldBeOfType<BooleanValueValidator>(); |
||||
|
|
||||
|
featureListDto2.Features[1].ValueType.ShouldBeOfType<SelectionStringValueType>(); |
||||
|
featureListDto2.Features[1].ValueType.Validator.ShouldBeOfType<AlwaysValidValueValidator>(); |
||||
|
featureListDto2.Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldBeOfType<LocalizableSelectionStringValueItem[]>(); |
||||
|
featureListDto2.Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldContain(x => |
||||
|
x.Value == "TestValue" && x.DisplayText.ResourceName == "TestResourceName" && |
||||
|
x.DisplayText.Name == "TestName"); |
||||
|
|
||||
|
featureListDto2.Features[2].ValueType.ShouldBeOfType<ToggleStringValueType>(); |
||||
|
featureListDto2.Features[2].ValueType.Validator.ShouldBeOfType<NumericValueValidator>(); |
||||
|
featureListDto2.Features[2].ValueType.Validator.As<NumericValueValidator>().MaxValue.ShouldBe(1000); |
||||
|
featureListDto2.Features[2].ValueType.Validator.As<NumericValueValidator>().MinValue.ShouldBe(10); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,25 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework> |
|
||||
<AssemblyName>Volo.Abp.Users.EntityFrameworkCore.Tests</AssemblyName> |
|
||||
<PackageId>Volo.Abp.Users.EntityFrameworkCore.Tests</PackageId> |
|
||||
<LangVersion>latest</LangVersion> |
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> |
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\src\Volo.Abp.Users.EntityFrameworkCore\Volo.Abp.Users.EntityFrameworkCore.csproj" /> |
|
||||
<ProjectReference Include="..\..\test\Volo.Abp.Users.Tests.Shared\Volo.Abp.Users.Tests.Shared.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> |
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.4" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,2 +0,0 @@ |
|||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|
||||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
|
||||
@ -1,7 +0,0 @@ |
|||||
namespace Volo.Abp.Users.EntityFrameworkCore |
|
||||
{ |
|
||||
public class AbpUserRepository_Tests : AbpUserRepository_Tests<AbpUsersEntityFrameworkCoreTestModule> |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
using System; |
|
||||
using Microsoft.EntityFrameworkCore; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Volo.Abp.EntityFrameworkCore; |
|
||||
using Volo.Abp.Modularity; |
|
||||
using Volo.Abp.Uow; |
|
||||
|
|
||||
namespace Volo.Abp.Users.EntityFrameworkCore |
|
||||
{ |
|
||||
[DependsOn( |
|
||||
typeof(AbpUsersTestsSharedModule), |
|
||||
typeof(AbpUsersEntityFrameworkCoreModule) |
|
||||
)] |
|
||||
public class AbpUsersEntityFrameworkCoreTestModule : AbpModule |
|
||||
{ |
|
||||
public override void ConfigureServices(IServiceCollection services) |
|
||||
{ |
|
||||
services.AddEntityFrameworkInMemoryDatabase(); |
|
||||
|
|
||||
var databaseName = Guid.NewGuid().ToString(); |
|
||||
|
|
||||
services.Configure<AbpDbContextOptions>(options => |
|
||||
{ |
|
||||
options.Configure(context => |
|
||||
{ |
|
||||
context.DbContextOptions.UseInMemoryDatabase(databaseName); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
services.Configure<AbpUnitOfWorkDefaultOptions>(options => |
|
||||
{ |
|
||||
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions
|
|
||||
}); |
|
||||
|
|
||||
services.AddAssemblyOf<AbpUsersEntityFrameworkCoreTestModule>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,7 +0,0 @@ |
|||||
namespace Volo.Abp.Users.EntityFrameworkCore |
|
||||
{ |
|
||||
public class ExternalUserLookupService_Tests : ExternalUserLookupService_Tests<AbpUsersEntityFrameworkCoreTestModule> |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -1,7 +0,0 @@ |
|||||
namespace Volo.Abp.Users.EntityFrameworkCore |
|
||||
{ |
|
||||
public class LocalUserLookupService_Tests : LocalUserLookupService_Tests<AbpUsersEntityFrameworkCoreTestModule> |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework> |
|
||||
<AssemblyName>Volo.Abp.Users.MongoDB.Tests</AssemblyName> |
|
||||
<PackageId>Volo.Abp.Users.MongoDB.Tests</PackageId> |
|
||||
<LangVersion>latest</LangVersion> |
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> |
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\src\Volo.Abp.Users.MongoDB\Volo.Abp.Users.MongoDB.csproj" /> |
|
||||
<ProjectReference Include="..\..\test\Volo.Abp.Users.Tests.Shared\Volo.Abp.Users.Tests.Shared.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> |
|
||||
<PackageReference Include="Mongo2Go" Version="2.2.12" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||