Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
wellgenio committed Aug 27, 2024
2 parents cb4123e + e13ff73 commit e2fd710
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 16 deletions.
8 changes: 8 additions & 0 deletions lib/src/lucid_validation_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ abstract class LucidValidationBuilder<TProp, Entity> {
/// The [key] can be used to identify this specific validation in a larger validation context.
LucidValidationBuilder(this.key, this._selector);

String? Function([String?]) nestedByField(Entity entity, String key) {
if (_nestedValidator == null) {
return ([_]) => null;
}

return _nestedValidator!.byField(_selector(entity), key);
}

/// Registers a validation rule for the property.
///
/// [validator] is a function that returns `true` if the property is valid and `false` otherwise.
Expand Down
48 changes: 32 additions & 16 deletions lib/src/lucid_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ abstract class LucidValidator<E> {
/// final validator = UserValidation();
/// validator.ruleFor((user) => user.email).validEmail();
/// ```
LucidValidationBuilder<TProp, E> ruleFor<TProp>(TProp Function(E entity) selector, {String key = ''}) {
LucidValidationBuilder<TProp, E> ruleFor<TProp>(TProp Function(E entity) selector, {required String key}) {
final builder = _LucidValidationBuilder<TProp, E>(key, selector);
_builders.add(builder);

Expand All @@ -36,23 +36,39 @@ abstract class LucidValidator<E> {
/// String? validationResult = emailValidator('[email protected]');
/// ```
String? Function([String?]) byField(E entity, String key) {
final builder = _builders
if (key.contains('.')) {
final keys = key.split('.');

final firstKey = keys.removeAt(0);
final builder = _getBuilderByKey(firstKey);
if (builder == null) {
return ([_]) => null;
}

return builder.nestedByField(entity, keys.join('.'));
} else {
final builder = _getBuilderByKey(key);

if (builder == null) {
return ([_]) => null;
}

return ([_]) {
final errors = builder.executeRules(entity);
if (errors.isNotEmpty) {
return errors.first.message;
}
return null;
};
}
}

LucidValidationBuilder? _getBuilderByKey(String key) {
return _builders
.where(
(builder) => builder.key == key,
)
.firstOrNull;

if (builder == null) {
return ([_]) => null;
}

return ([_]) {
final errors = builder.executeRules(entity);
if (errors.isNotEmpty) {
return errors.first.message;
}
return null;
};
}

/// Validates the entire entity [E] and returns a list of [ValidationError]s if any rules fail.
Expand All @@ -70,8 +86,8 @@ abstract class LucidValidator<E> {
/// }
/// ```
ValidationResult validate(E entity) {
final errors = _builders.fold(<ValidationError>[], (previousErrors, errors) {
return previousErrors..addAll(errors.executeRules(entity));
final errors = _builders.fold(<ValidationError>[], (previousErrors, builder) {
return previousErrors..addAll(builder.executeRules(entity));
});

return ValidationResult(
Expand Down
118 changes: 118 additions & 0 deletions test/byfield_validation_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:lucid_validation/lucid_validation.dart';
import 'package:test/test.dart';

import 'mocks/mocks.dart';

void main() {
test('byfield normal', () {
final validator = TestLucidValidator<UserModel>();

validator.ruleFor((model) => model.age, key: 'age').greaterThan(17);
validator.ruleFor((model) => model.email, key: 'email').notEmpty();

final user = UserModel()..age = 16;

final ageValidator = validator.byField(user, 'age');

expect(ageValidator(), isNotNull);

user.age = 18;

expect(ageValidator(), null);

final emailValidator = validator.byField(user, 'email');

expect(emailValidator(), isNotNull);

user.email = '[email protected]';

expect(emailValidator(), null);
});

group('nested byfield normal', () {
final flagValidator = TestLucidValidator<Flag>();
flagValidator.ruleFor((flag) => flag.value, key: 'value').equalTo((entity) => true);

final categoryValidator = TestLucidValidator<CategoryModel>();
categoryValidator.ruleFor((category) => category.name, key: 'name').notEmpty();
categoryValidator.ruleFor((category) => category.flag, key: 'flag').setValidator(flagValidator);

final productValidator = TestLucidValidator<ProductModel>();
productValidator.ruleFor((product) => product.name, key: 'name').notEmpty();
productValidator.ruleFor((product) => product.price, key: 'price').greaterThan(0);
productValidator.ruleFor((product) => product.category, key: 'category').setValidator(categoryValidator);

final product = ProductModel();

test('nameValidator', () {
final nameValidator = productValidator.byField(product, 'name');
expect(nameValidator(), isNotNull);
});

test('priceValidator', () {
final priceValidator = productValidator.byField(product, 'price');
expect(priceValidator(), isNotNull);
});

test('categoryValidator', () {
final categoryValidatorFn = productValidator.byField(product, 'category');
expect(categoryValidatorFn(), isNotNull);
});

test('categoryNameValidator', () {
final categoryNameValidatorFn = productValidator.byField(product, 'category.name');
expect(categoryNameValidatorFn(), isNotNull);
});

test('flagValidator', () {
final flagValidatorFn = productValidator.byField(product, 'category.flag');
expect(flagValidatorFn(), isNotNull);
});

test('flagValueValidator', () {
final flagValueValidatorFn = productValidator.byField(product, 'category.flag.value');
expect(flagValueValidatorFn(), isNotNull);
});

test('must return null if not found key', () {
final flagValueValidatorFn = productValidator.byField(product, 'category.flag.notExists');
expect(flagValueValidatorFn(), null);
});

test('all validators', () {
product.name = 'Product';
product.price = 10;
product.category.name = 'Category';
product.category.flag.value = true;

final nameValidator = productValidator.byField(product, 'name');
final priceValidator = productValidator.byField(product, 'price');
final categoryValidatorFn = productValidator.byField(product, 'category');
final categoryNameValidatorFn = productValidator.byField(product, 'category.name');
final flagValidatorFn = productValidator.byField(product, 'category.flag');
final flagValueValidatorFn = productValidator.byField(product, 'category.flag.value');

expect(nameValidator(), null);
expect(priceValidator(), null);
expect(categoryValidatorFn(), null);
expect(categoryNameValidatorFn(), null);
expect(flagValidatorFn(), null);
expect(flagValueValidatorFn(), null);
});
});
}

class ProductModel {
String name = '';
double price = 0.0;
CategoryModel category = CategoryModel();
}

class CategoryModel {
String name = '';
Flag flag = Flag();
}

class Flag {
bool value = false;
}

0 comments on commit e2fd710

Please sign in to comment.