Migrated json_god
This commit is contained in:
parent
d211fc2c3d
commit
5e9172aca9
20 changed files with 161 additions and 137 deletions
23
CHANGELOG.md
23
CHANGELOG.md
|
@ -2,7 +2,7 @@
|
|||
* Changed Dart SDK requirements for all packages to ">=2.12.0 <3.0.0" to support NNBD.
|
||||
* Updated pretty_logging to 3.0.0 (0/0 tests)
|
||||
* Updated angel_http_exception to 3.0.0 (0/0 tests)
|
||||
* Moved angel_cli to https://github.com/dukefirehawk/cli
|
||||
* Moved angel_cli to https://github.com/dukefirehawk/cli (Not migrated)
|
||||
* Added code_buffer and updated to 2.0.0 (16/16 tests)
|
||||
* Added combinator and updated to 2.0.0 (16/16 tests)
|
||||
* Updated angel_route to 5.0.0 (35/35 tests passed)
|
||||
|
@ -12,7 +12,20 @@
|
|||
* Added mock_request and updated to 2.0.0 (0/0 tests)
|
||||
* Updated angel_framework to 4.0.0 (146/149 tests passed)
|
||||
* Updated angel_auth to 4.0.0 (22/32 test passed)
|
||||
* Updated angel_configuration to 4.0.0 (In progress)
|
||||
* Updated angel_configuration to 4.0.0 (6/8 test passed)
|
||||
* Updated angel_validate to 4.0.0 (6/7 test passed)
|
||||
* Updated json_god to 4.0.0 (13/13 test passed)
|
||||
* Updated angel_client to 3.0.0 (in progress)
|
||||
* Updated angel_websocket to 3.0.0 (in progress)
|
||||
* Updated test to 3.0.0 (in progress)
|
||||
* Updated jael to 3.0.0 (in progress)
|
||||
* Updated jael_preprocessor to 3.0.0 (in progress)
|
||||
* Updated angel_jael to 3.0.0 (in progress)
|
||||
* Updated pub_sub to 3.0.0 (in progress)
|
||||
* Updated production to 2.0.0 (in progress)
|
||||
* Updated hot to 3.0.0 (in progress)
|
||||
* Updated static to 3.0.0 (in progress)
|
||||
* Update basic-sdk-2.12.x boilerplate (in progress)
|
||||
|
||||
# 3.0.0 (Non NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
|
||||
|
@ -25,12 +38,12 @@
|
|||
* Updated angel_framework to 3.0.0
|
||||
* Updated angel_auth to 3.0.0
|
||||
* Updated angel_configuration to 3.0.0
|
||||
* Updated jael to 3.0.0
|
||||
* Updated jael_preprocessor to 3.0.0
|
||||
* Updated validate to 3.0.0
|
||||
* Updated angel_validate to 3.0.0
|
||||
* Added and updated json_god to 3.0.0
|
||||
* Updated angel_client to 3.0.0
|
||||
* Updated angel_websocket to 3.0.0 (one issue to be resolved)
|
||||
* Updated jael to 3.0.0
|
||||
* Updated jael_preprocessor to 3.0.0
|
||||
* Updated test to 3.0.0
|
||||
* Updated angel_jael to 3.0.0 (Issue with 2 dependencies)
|
||||
* Added pub_sub and updated to 3.0.0
|
||||
|
|
|
@ -44,7 +44,7 @@ Future<void> _loadYamlFile(Map map, File yamlFile, Map<String, String> env,
|
|||
}
|
||||
}
|
||||
|
||||
for (var key in configMap.keys as Iterable<String>) {
|
||||
for (var key in configMap.keys) {
|
||||
out[key] = _applyEnv(configMap[key], env, warn);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/// A robust library for JSON serialization and deserialization.
|
||||
library json_god;
|
||||
|
||||
import 'package:dart2_constant/convert.dart';
|
||||
//import 'package:dart2_constant/convert.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'src/reflection.dart' as reflection;
|
||||
|
||||
|
@ -14,4 +15,4 @@ part 'src/util.dart';
|
|||
@deprecated
|
||||
bool debug = false;
|
||||
|
||||
final Logger logger = new Logger('json_god');
|
||||
final Logger logger = new Logger('json_god');
|
||||
|
|
|
@ -3,14 +3,14 @@ part of json_god;
|
|||
/// Deserializes a JSON string into a Dart datum.
|
||||
///
|
||||
/// You can also provide an output Type to attempt to serialize the JSON into.
|
||||
deserialize(String json, {Type outputType}) {
|
||||
deserialize(String json, {Type? outputType}) {
|
||||
var deserialized = deserializeJson(json, outputType: outputType);
|
||||
logger.info("Deserialization result: $deserialized");
|
||||
return deserialized;
|
||||
}
|
||||
|
||||
/// Deserializes JSON into data, without validating it.
|
||||
deserializeJson(String s, {Type outputType}) {
|
||||
deserializeJson(String s, {Type? outputType}) {
|
||||
logger.info("Deserializing the following JSON: $s");
|
||||
|
||||
if (outputType == null) {
|
||||
|
@ -23,7 +23,7 @@ deserializeJson(String s, {Type outputType}) {
|
|||
}
|
||||
|
||||
/// Deserializes some JSON-serializable value into a usable Dart value.
|
||||
deserializeDatum(value, {Type outputType}) {
|
||||
deserializeDatum(value, {Type? outputType}) {
|
||||
if (outputType != null) {
|
||||
return reflection.deserialize(value, outputType, deserializeDatum);
|
||||
} else if (value is List) {
|
||||
|
|
|
@ -7,7 +7,7 @@ const Symbol hashCodeSymbol = #hashCode;
|
|||
const Symbol runtimeTypeSymbol = #runtimeType;
|
||||
|
||||
typedef Serializer(value);
|
||||
typedef Deserializer(value, {Type outputType});
|
||||
typedef Deserializer(value, {Type? outputType});
|
||||
|
||||
List<Symbol> _findGetters(ClassMirror classMirror) {
|
||||
List<Symbol> result = [];
|
||||
|
@ -110,7 +110,7 @@ _deserializeFromJsonByReflection(
|
|||
throw new ArgumentError('$outputType is not a class.');
|
||||
}
|
||||
|
||||
var type = typeMirror as ClassMirror;
|
||||
var type = typeMirror;
|
||||
var fromJson =
|
||||
new Symbol('${MirrorSystem.getName(type.simpleName)}.fromJson');
|
||||
|
||||
|
@ -166,7 +166,7 @@ _deserializeFromJsonByReflection(
|
|||
Symbol symbolForGetter = classMirror.instanceMembers.keys
|
||||
.firstWhere((x) => x == searchSymbol);
|
||||
Type requiredType = classMirror
|
||||
.instanceMembers[symbolForGetter].returnType.reflectedType;
|
||||
.instanceMembers[symbolForGetter]!.returnType.reflectedType;
|
||||
if (data[key].runtimeType != requiredType) {
|
||||
logger.info("Currently, $key is a ${data[key].runtimeType}.");
|
||||
logger.info("However, $key must be a $requiredType.");
|
||||
|
|
|
@ -3,23 +3,23 @@ part of json_god;
|
|||
/// Serializes any arbitrary Dart datum to JSON. Supports schema validation.
|
||||
String serialize(value) {
|
||||
var serialized = serializeObject(value);
|
||||
logger.info('Serialization result: $serialized');
|
||||
logger.info('Serialization result: $serialized');
|
||||
return json.encode(serialized);
|
||||
}
|
||||
|
||||
/// Transforms any Dart datum into a value acceptable to json.encode.
|
||||
serializeObject(value) {
|
||||
if (_isPrimitive(value)) {
|
||||
logger.info("Serializing primitive value: $value");
|
||||
logger.info("Serializing primitive value: $value");
|
||||
return value;
|
||||
} else if (value is DateTime) {
|
||||
logger.info("Serializing this DateTime: $value");
|
||||
logger.info("Serializing this DateTime: $value");
|
||||
return value.toIso8601String();
|
||||
} else if (value is Iterable) {
|
||||
logger.info("Serializing this Iterable: $value");
|
||||
logger.info("Serializing this Iterable: $value");
|
||||
return value.map(serializeObject).toList();
|
||||
} else if (value is Map) {
|
||||
logger.info("Serializing this Map: $value");
|
||||
logger.info("Serializing this Map: $value");
|
||||
return serializeMap(value);
|
||||
} else
|
||||
return serializeObject(reflection.serialize(value, serializeObject));
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
name: json_god
|
||||
version: 3.0.0
|
||||
version: 4.0.0
|
||||
authors:
|
||||
- Tobe O <thosakwe@gmail.com>
|
||||
description: Easy JSON serialization and deserialization in Dart.
|
||||
homepage: https://github.com/thosakwe/json_god
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
dart2_constant: ^1.0.0
|
||||
logging: ^1.0.0
|
||||
#dart2_constant: ^1.0.0
|
||||
logging: ^1.0.1
|
||||
dev_dependencies:
|
||||
stack_trace: ^1.0.0
|
||||
test: any
|
||||
stack_trace: ^1.10.0
|
||||
test: ^1.16.8
|
|
@ -47,15 +47,15 @@ testDeserializationOfMaps() {
|
|||
}
|
||||
|
||||
class Pokedex {
|
||||
Map<String, int> pokemon;
|
||||
Map<String, int>? pokemon;
|
||||
}
|
||||
|
||||
testDeserializationOfMapsWithReflection() {
|
||||
var s = '{"pokemon": {"Bulbasaur": 1, "Deoxys": 382}}';
|
||||
var pokedex = god.deserialize(s, outputType: Pokedex) as Pokedex;
|
||||
expect(pokedex.pokemon, hasLength(2));
|
||||
expect(pokedex.pokemon['Bulbasaur'], 1);
|
||||
expect(pokedex.pokemon['Deoxys'], 382);
|
||||
expect(pokedex.pokemon!['Bulbasaur'], 1);
|
||||
expect(pokedex.pokemon!['Deoxys'], 382);
|
||||
}
|
||||
|
||||
testDeserializationOfListsAsWellAsViaReflection() {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:dart2_constant/convert.dart';
|
||||
//import 'package:dart2_constant/convert.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:json_god/json_god.dart' as god;
|
||||
import 'package:test/test.dart';
|
||||
import 'shared.dart';
|
||||
|
|
|
@ -5,20 +5,20 @@ import 'package:stack_trace/stack_trace.dart';
|
|||
void printRecord(LogRecord rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
if (rec.stackTrace != null) print(new Chain.forTrace(rec.stackTrace).terse);
|
||||
if (rec.stackTrace != null) print(new Chain.forTrace(rec.stackTrace!).terse);
|
||||
}
|
||||
|
||||
class SampleNestedClass {
|
||||
String bar;
|
||||
String? bar;
|
||||
|
||||
SampleNestedClass([String this.bar]);
|
||||
SampleNestedClass([String? this.bar]);
|
||||
}
|
||||
|
||||
class SampleClass {
|
||||
String hello;
|
||||
String? hello;
|
||||
List<SampleNestedClass> nested = [];
|
||||
|
||||
SampleClass([String this.hello]);
|
||||
SampleClass([String? this.hello]);
|
||||
}
|
||||
|
||||
@WithSchemaUrl(
|
||||
|
|
|
@ -20,7 +20,7 @@ main() {
|
|||
}
|
||||
|
||||
class Foo {
|
||||
String text;
|
||||
String? text;
|
||||
|
||||
String get foo => 'poo$text';
|
||||
|
||||
|
|
|
@ -55,17 +55,19 @@ RequestHandler validate(Validator validator,
|
|||
{String errorMessage = 'Invalid data.'}) {
|
||||
return (RequestContext req, res) async {
|
||||
await req.parseBody();
|
||||
var result = await asyncApplyValidator(validator, req.bodyAsMap, req.app);
|
||||
var app = req.app;
|
||||
if (app != null) {
|
||||
var result = await asyncApplyValidator(validator, req.bodyAsMap, app);
|
||||
|
||||
if (result.errors.isNotEmpty) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: errorMessage, errors: result.errors);
|
||||
if (result.errors.isNotEmpty) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: errorMessage, errors: result.errors);
|
||||
}
|
||||
|
||||
req.bodyAsMap
|
||||
..clear()
|
||||
..addAll(result.data);
|
||||
}
|
||||
|
||||
req.bodyAsMap
|
||||
..clear()
|
||||
..addAll(result.data);
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
@ -75,18 +77,20 @@ RequestHandler validate(Validator validator,
|
|||
RequestHandler validateQuery(Validator validator,
|
||||
{String errorMessage = 'Invalid data.'}) {
|
||||
return (RequestContext req, res) async {
|
||||
var result =
|
||||
await asyncApplyValidator(validator, req.queryParameters, req.app);
|
||||
var app = req.app;
|
||||
if (app != null) {
|
||||
var result =
|
||||
await asyncApplyValidator(validator, req.queryParameters, app);
|
||||
|
||||
if (result.errors.isNotEmpty) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: errorMessage, errors: result.errors);
|
||||
if (result.errors.isNotEmpty) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: errorMessage, errors: result.errors);
|
||||
}
|
||||
|
||||
req.queryParameters
|
||||
..clear()
|
||||
..addAll(result.data);
|
||||
}
|
||||
|
||||
req.queryParameters
|
||||
..clear()
|
||||
..addAll(result.data);
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
@ -96,17 +100,19 @@ RequestHandler validateQuery(Validator validator,
|
|||
HookedServiceEventListener validateEvent(Validator validator,
|
||||
{String errorMessage = 'Invalid data.'}) {
|
||||
return (HookedServiceEvent e) async {
|
||||
var result = await asyncApplyValidator(
|
||||
validator, e.data as Map, (e.request?.app ?? e.service.app));
|
||||
var app = e.request?.app ?? e.service.app;
|
||||
if (app != null) {
|
||||
var result = await asyncApplyValidator(validator, e.data as Map, app);
|
||||
|
||||
if (result.errors.isNotEmpty) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: errorMessage, errors: result.errors);
|
||||
if (result.errors.isNotEmpty) {
|
||||
throw AngelHttpException.badRequest(
|
||||
message: errorMessage, errors: result.errors);
|
||||
}
|
||||
|
||||
e.data
|
||||
..clear()
|
||||
..addAll(result.data);
|
||||
}
|
||||
|
||||
e.data
|
||||
..clear()
|
||||
..addAll(result.data);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -122,7 +128,7 @@ Future<ValidationResult> asyncApplyValidator(
|
|||
var value = result.data[key];
|
||||
var description = StringDescription("'$key': expected ");
|
||||
|
||||
for (var rule in validator.rules[key]) {
|
||||
for (var rule in validator.rules[key]!) {
|
||||
if (rule is AngelMatcher) {
|
||||
var r = await rule.matchesWithAngel(value, key, result.data, {}, app);
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ AngelMatcher matchAsync(FutureOr<Matcher> Function(String, Object) matcher,
|
|||
/// Returns an [AngelMatcher] that verifies that an item with the given [idField]
|
||||
/// exists in the service at [servicePath], without throwing a `404` or returning `null`.
|
||||
AngelMatcher idExistsInService(String servicePath,
|
||||
{String idField = 'id', String description}) {
|
||||
{String idField = 'id', String? description}) {
|
||||
return predicateWithAngel(
|
||||
(key, item, app) async {
|
||||
try {
|
||||
|
@ -108,14 +108,13 @@ class _MatchWithAngel extends AngelMatcher {
|
|||
_MatchWithAngel(this.f, this.description);
|
||||
|
||||
@override
|
||||
Description describe(Description description) => this.description == null
|
||||
? description
|
||||
: description.add(this.description);
|
||||
Description describe(Description description) =>
|
||||
description.add(this.description);
|
||||
|
||||
@override
|
||||
Future<bool> matchesWithAngel(
|
||||
item, String key, Map context, Map matchState, Angel app) {
|
||||
return Future.sync(() => f(item, context, app)).then((result) {
|
||||
return Future.sync(() => f(item as Object, context, app)).then((result) {
|
||||
return result.matches(item, matchState);
|
||||
});
|
||||
}
|
||||
|
@ -128,14 +127,13 @@ class _PredicateWithAngel extends AngelMatcher {
|
|||
_PredicateWithAngel(this.predicate, this.description);
|
||||
|
||||
@override
|
||||
Description describe(Description description) => this.description == null
|
||||
? description
|
||||
: description.add(this.description);
|
||||
Description describe(Description description) =>
|
||||
description.add(this.description);
|
||||
|
||||
@override
|
||||
Future<bool> matchesWithAngel(
|
||||
item, String key, Map context, Map matchState, Angel app) {
|
||||
return Future<bool>.sync(() => predicate(key, item, app));
|
||||
return Future<bool>.sync(() => predicate(key, item as Object, app));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,15 +145,14 @@ class _MatchAsync extends AngelMatcher {
|
|||
_MatchAsync(this.matcher, this.feature, this.description);
|
||||
|
||||
@override
|
||||
Description describe(Description description) => this.description == null
|
||||
? description
|
||||
: description.add(this.description);
|
||||
Description describe(Description description) =>
|
||||
description.add(this.description);
|
||||
|
||||
@override
|
||||
Future<bool> matchesWithAngel(
|
||||
item, String key, Map context, Map matchState, Angel app) async {
|
||||
var f = await feature();
|
||||
var m = await matcher(key, f);
|
||||
var m = await matcher(key, f as Object);
|
||||
var c = wrapAngelMatcher(m);
|
||||
return await c.matchesWithAngel(item, key, context, matchState, app);
|
||||
}
|
||||
|
|
|
@ -44,10 +44,9 @@ class _PredicateWithContext extends ContextAwareMatcher {
|
|||
_PredicateWithContext(this.f, this.desc);
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
desc == null ? description : description.add(desc);
|
||||
Description describe(Description description) => description.add(desc);
|
||||
|
||||
@override
|
||||
bool matchesWithContext(item, String key, Map context, Map matchState) =>
|
||||
f(item, key, context, matchState);
|
||||
f(item as Object, key, context, matchState);
|
||||
}
|
||||
|
|
|
@ -11,34 +11,36 @@ final RegExp _url = RegExp(
|
|||
|
||||
/// Asserts that a `String` is alphanumeric, but also lets it contain dashes or underscores.
|
||||
final Matcher isAlphaDash = predicate(
|
||||
(value) => value is String && _alphaDash.hasMatch(value),
|
||||
(dynamic value) => value is String && _alphaDash.hasMatch(value),
|
||||
'alphanumeric (dashes and underscores are allowed)');
|
||||
|
||||
/// Asserts that a `String` is alphanumeric, but also lets it contain dashes or underscores.
|
||||
///
|
||||
final Matcher isAlphaNum = predicate(
|
||||
(value) => value is String && _alphaNum.hasMatch(value), 'alphanumeric');
|
||||
(dynamic value) => value is String && _alphaNum.hasMatch(value),
|
||||
'alphanumeric');
|
||||
|
||||
/// Asserts that a value either equals `true` or `false`.
|
||||
final Matcher isBool = predicate((value) => value is bool, 'a bool');
|
||||
final Matcher isBool = predicate((dynamic value) => value is bool, 'a bool');
|
||||
|
||||
/// Asserts that a `String` complies to the RFC 5322 e-mail standard.
|
||||
final Matcher isEmail = predicate(
|
||||
(value) => value is String && _email.hasMatch(value),
|
||||
(dynamic value) => value is String && _email.hasMatch(value),
|
||||
'a valid e-mail address');
|
||||
|
||||
/// Asserts that a value is an `int`.
|
||||
final Matcher isInt = predicate((value) => value is int, 'an integer');
|
||||
final Matcher isInt = predicate((dynamic value) => value is int, 'an integer');
|
||||
|
||||
/// Asserts that a value is a `num`.
|
||||
final Matcher isNum = predicate((value) => value is num, 'a number');
|
||||
final Matcher isNum = predicate((dynamic value) => value is num, 'a number');
|
||||
|
||||
/// Asserts that a value is a `String`.
|
||||
final Matcher isString = predicate((value) => value is String, 'a string');
|
||||
final Matcher isString =
|
||||
predicate((dynamic value) => value is String, 'a string');
|
||||
|
||||
/// Asserts that a value is a non-empty `String`.
|
||||
final Matcher isNonEmptyString = predicate(
|
||||
(value) => value is String && value.trim().isNotEmpty,
|
||||
(dynamic value) => value is String && value.trim().isNotEmpty,
|
||||
'a non-empty string');
|
||||
|
||||
/// Asserts that a value, presumably from a checkbox, is positive.
|
||||
|
@ -47,9 +49,9 @@ final Matcher isChecked =
|
|||
|
||||
/// Ensures that a string is an ISO-8601 date string.
|
||||
final Matcher isIso8601DateString = predicate(
|
||||
(x) {
|
||||
(dynamic x) {
|
||||
try {
|
||||
return x is String && DateTime.parse(x) != null;
|
||||
return x is String;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
|
@ -64,7 +66,7 @@ final Matcher isIso8601DateString = predicate(
|
|||
/// https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)
|
||||
/// ```
|
||||
final Matcher isUrl = predicate(
|
||||
(value) => value is String && _url.hasMatch(value),
|
||||
(dynamic value) => value is String && _url.hasMatch(value),
|
||||
'a valid url, starting with http:// or https://');
|
||||
|
||||
/// Use [isUrl] instead.
|
||||
|
@ -73,12 +75,12 @@ final Matcher isurl = isUrl;
|
|||
|
||||
/// Enforces a minimum length on a string.
|
||||
Matcher minLength(int length) => predicate(
|
||||
(value) => value is String && value.length >= length,
|
||||
(dynamic value) => value is String && value.length >= length,
|
||||
'a string at least $length character(s) long');
|
||||
|
||||
/// Limits the maximum length of a string.
|
||||
Matcher maxLength(int length) => predicate(
|
||||
(value) => value is String && value.length <= length,
|
||||
(dynamic value) => value is String && value.length <= length,
|
||||
'a string no longer than $length character(s) long');
|
||||
|
||||
/// Asserts that for a key `x`, the context contains an identical item `x_confirmed`.
|
||||
|
|
|
@ -8,17 +8,17 @@ final RegExp _forbidden = RegExp(r'!$');
|
|||
final RegExp _optional = RegExp(r'\?$');
|
||||
|
||||
/// Returns a value based the result of a computation.
|
||||
typedef DefaultValueFunction();
|
||||
typedef DefaultValueFunction = Function();
|
||||
|
||||
/// Generates an error message based on the given input.
|
||||
typedef String CustomErrorMessageFunction(item);
|
||||
typedef CustomErrorMessageFunction = String Function(dynamic item);
|
||||
|
||||
/// Determines if a value is valid.
|
||||
typedef bool Filter(value);
|
||||
typedef Filter = bool Function(dynamic value);
|
||||
|
||||
/// Converts the desired fields to their numeric representations, if present.
|
||||
Map<String, dynamic> autoParse(Map inputData, Iterable<String> fields) {
|
||||
Map<String, dynamic> data = {};
|
||||
var data = <String, dynamic>{};
|
||||
|
||||
for (var key in inputData.keys) {
|
||||
if (!fields.contains(key)) {
|
||||
|
@ -83,7 +83,7 @@ class Validator extends Matcher {
|
|||
schema[keys] is Iterable ? schema[keys] : [schema[keys]];
|
||||
var iterable = [];
|
||||
|
||||
_addTo(x) {
|
||||
void _addTo(x) {
|
||||
if (x is Iterable) {
|
||||
x.forEach(_addTo);
|
||||
} else {
|
||||
|
@ -111,8 +111,8 @@ class Validator extends Matcher {
|
|||
Validator(Map<String, dynamic> schema,
|
||||
{Map<String, dynamic> defaultValues = const {},
|
||||
Map<String, dynamic> customErrorMessages = const {}}) {
|
||||
this.defaultValues.addAll(defaultValues ?? {});
|
||||
this.customErrorMessages.addAll(customErrorMessages ?? {});
|
||||
this.defaultValues.addAll(defaultValues);
|
||||
this.customErrorMessages.addAll(customErrorMessages);
|
||||
_importSchema(schema);
|
||||
}
|
||||
|
||||
|
@ -121,18 +121,18 @@ class Validator extends Matcher {
|
|||
|
||||
/// Validates, and filters input data.
|
||||
ValidationResult check(Map inputData) {
|
||||
List<String> errors = [];
|
||||
var errors = <String>[];
|
||||
var input = Map.from(inputData);
|
||||
Map<String, dynamic> data = {};
|
||||
var data = <String, dynamic>{};
|
||||
|
||||
for (String key in defaultValues.keys) {
|
||||
for (var key in defaultValues.keys) {
|
||||
if (!input.containsKey(key)) {
|
||||
var value = defaultValues[key];
|
||||
input[key] = value is DefaultValueFunction ? value() : value;
|
||||
}
|
||||
}
|
||||
|
||||
for (String field in forbiddenFields) {
|
||||
for (var field in forbiddenFields) {
|
||||
if (input.containsKey(field)) {
|
||||
if (!customErrorMessages.containsKey(field)) {
|
||||
errors.add("'$field' is forbidden.");
|
||||
|
@ -142,7 +142,7 @@ class Validator extends Matcher {
|
|||
}
|
||||
}
|
||||
|
||||
for (String field in requiredFields) {
|
||||
for (var field in requiredFields) {
|
||||
if (!_hasContextValidators(rules[field] ?? [])) {
|
||||
if (!input.containsKey(field)) {
|
||||
if (!customErrorMessages.containsKey(field)) {
|
||||
|
@ -162,7 +162,7 @@ class Validator extends Matcher {
|
|||
var value = input[key];
|
||||
var description = StringDescription("'$key': expected ");
|
||||
|
||||
for (var matcher in rules[key]) {
|
||||
for (var matcher in rules[key]!) {
|
||||
if (matcher is ContextValidator) {
|
||||
if (!matcher.validate(key, input)) {
|
||||
errors.add(matcher
|
||||
|
@ -175,7 +175,7 @@ class Validator extends Matcher {
|
|||
}
|
||||
|
||||
if (valid) {
|
||||
for (Matcher matcher in rules[key]) {
|
||||
for (var matcher in rules[key]!) {
|
||||
try {
|
||||
if (matcher is Validator) {
|
||||
var result = matcher.check(value as Map);
|
||||
|
@ -271,12 +271,12 @@ class Validator extends Matcher {
|
|||
{Map<String, dynamic> defaultValues = const {},
|
||||
Map<String, dynamic> customErrorMessages = const {},
|
||||
bool overwrite = false}) {
|
||||
Map<String, dynamic> _schema = {};
|
||||
var _schema = <String, dynamic>{};
|
||||
var child = Validator.empty()
|
||||
..defaultValues.addAll(this.defaultValues)
|
||||
..defaultValues.addAll(defaultValues ?? {})
|
||||
..defaultValues.addAll(defaultValues)
|
||||
..customErrorMessages.addAll(this.customErrorMessages)
|
||||
..customErrorMessages.addAll(customErrorMessages ?? {})
|
||||
..customErrorMessages.addAll(customErrorMessages)
|
||||
..requiredFields.addAll(requiredFields)
|
||||
..rules.addAll(rules);
|
||||
|
||||
|
@ -320,7 +320,7 @@ class Validator extends Matcher {
|
|||
return;
|
||||
}
|
||||
|
||||
rules[key].add(rule);
|
||||
rules[key]!.add(rule);
|
||||
}
|
||||
|
||||
/// Adds all given [rules].
|
||||
|
@ -331,7 +331,7 @@ class Validator extends Matcher {
|
|||
/// Removes a [rule].
|
||||
void removeRule(String key, Matcher rule) {
|
||||
if (rules.containsKey(key)) {
|
||||
rules[key].remove(rule);
|
||||
rules[key]!.remove(rule);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,17 +377,19 @@ class ValidationResult {
|
|||
/// Occurs when user-provided data is invalid.
|
||||
class ValidationException extends AngelHttpException {
|
||||
/// A list of errors that resulted in the given data being marked invalid.
|
||||
@override
|
||||
final List<String> errors = [];
|
||||
|
||||
/// A descriptive message describing the error.
|
||||
@override
|
||||
final String message;
|
||||
|
||||
ValidationException(this.message, {Iterable<String> errors = const []})
|
||||
: super(FormatException(message),
|
||||
statusCode: 400,
|
||||
errors: (errors ?? <String>[]).toSet().toList(),
|
||||
errors: (errors).toSet().toList(),
|
||||
stackTrace: StackTrace.current) {
|
||||
if (errors != null) this.errors.addAll(errors.toSet());
|
||||
this.errors.addAll(errors.toSet());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -400,8 +402,10 @@ class ValidationException extends AngelHttpException {
|
|||
return 'Validation error: ${errors.first}';
|
||||
}
|
||||
|
||||
var messages = ['${errors.length} validation errors:\n']
|
||||
..addAll(errors.map((error) => '* $error'));
|
||||
var messages = [
|
||||
'${errors.length} validation errors:\n',
|
||||
...errors.map((error) => '* $error')
|
||||
];
|
||||
|
||||
return messages.join('\n');
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
name: angel_validate
|
||||
description: Cross-platform request body validation library based on `matcher`.
|
||||
version: 3.0.0
|
||||
version: 4.0.0
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/validate
|
||||
publish_to: none
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
angel_framework:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/framework
|
||||
angel_http_exception:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/http_exception
|
||||
matcher: ^0.12.0
|
||||
dev_dependencies:
|
||||
|
@ -26,7 +26,7 @@ dev_dependencies:
|
|||
# path: packages/test
|
||||
build_runner: ^1.11.1
|
||||
build_web_compilers: ^2.12.2
|
||||
# logging: ^0.11.0
|
||||
mock_request:
|
||||
# logging: ^0.11.0
|
||||
# mock_request:
|
||||
pedantic: ^1.0.0
|
||||
test: ^1.15.7
|
|
@ -43,7 +43,7 @@ main() {
|
|||
|
||||
test('comma in schema', () {
|
||||
expect(todoSchema.rules.keys, allOf(contains('foo'), contains('bar')));
|
||||
expect([todoSchema.rules['foo'].first, todoSchema.rules['bar'].first],
|
||||
everyElement(predicate((x) => x == isTrue)));
|
||||
expect([todoSchema.rules['foo']!.first, todoSchema.rules['bar']!.first],
|
||||
everyElement(predicate((dynamic x) => x == isTrue)));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,21 +18,21 @@ void printRecord(LogRecord rec) {
|
|||
}
|
||||
|
||||
void main() {
|
||||
Angel app;
|
||||
AngelHttp http;
|
||||
Angel? app;
|
||||
late AngelHttp http;
|
||||
//TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
app = Angel();
|
||||
http = AngelHttp(app, useZone: false);
|
||||
http = AngelHttp(app!, useZone: false);
|
||||
|
||||
app.chain([validate(echoSchema)]).post('/echo',
|
||||
app!.chain([validate(echoSchema)]).post('/echo',
|
||||
(RequestContext req, res) async {
|
||||
await req.parseBody();
|
||||
res.write('Hello, ${req.bodyAsMap['message']}!');
|
||||
});
|
||||
|
||||
app.logger = Logger('angel')..onRecord.listen(printRecord);
|
||||
app!.logger = Logger('angel')..onRecord.listen(printRecord);
|
||||
//client = await connectTo(app);
|
||||
});
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'dart:html';
|
|||
|
||||
import 'package:angel_validate/angel_validate.dart';
|
||||
|
||||
final $errors = querySelector('#errors') as UListElement;
|
||||
final $form = querySelector('#form') as FormElement;
|
||||
final $blank = querySelector('[name="blank"]') as InputElement;
|
||||
final $errors = querySelector('#errors') as UListElement?;
|
||||
final $form = querySelector('#form') as FormElement?;
|
||||
final $blank = querySelector('[name="blank"]') as InputElement?;
|
||||
|
||||
final Validator formSchema = Validator({
|
||||
'firstName*': [isString, isNotEmpty],
|
||||
|
@ -29,9 +29,9 @@ final Validator formSchema = Validator({
|
|||
});
|
||||
|
||||
main() {
|
||||
$form.onSubmit.listen((e) {
|
||||
$form!.onSubmit.listen((e) {
|
||||
e.preventDefault();
|
||||
$errors.children.clear();
|
||||
$errors!.children.clear();
|
||||
|
||||
var formData = {};
|
||||
|
||||
|
@ -39,7 +39,7 @@ main() {
|
|||
formData[key] = (querySelector('[name="$key"]') as InputElement).value;
|
||||
});
|
||||
|
||||
if ($blank.value.isNotEmpty) formData['blank'] = $blank.value;
|
||||
if ($blank!.value!.isNotEmpty) formData['blank'] = $blank!.value;
|
||||
|
||||
print('Form data: $formData');
|
||||
|
||||
|
@ -47,7 +47,7 @@ main() {
|
|||
var passportInfo =
|
||||
formSchema.enforceParsed(formData, ['age', 'familySize']);
|
||||
|
||||
$errors.children
|
||||
$errors!.children
|
||||
..add(success('Successfully registered for a passport.'))
|
||||
..add(success('First Name: ${passportInfo["firstName"]}'))
|
||||
..add(success('Last Name: ${passportInfo["lastName"]}'))
|
||||
|
@ -55,7 +55,7 @@ main() {
|
|||
..add(success(
|
||||
'Number of People in Family: ${passportInfo["familySize"]}'));
|
||||
} on ValidationException catch (e) {
|
||||
$errors.children.addAll(e.errors.map((error) {
|
||||
$errors!.children.addAll(e.errors.map((error) {
|
||||
return LIElement()..text = error;
|
||||
}));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue