Migrated json_god

This commit is contained in:
thomashii 2021-04-10 20:42:55 +08:00
parent d211fc2c3d
commit 5e9172aca9
20 changed files with 161 additions and 137 deletions

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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) {

View file

@ -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.");

View file

@ -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

View file

@ -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() {

View file

@ -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';

View file

@ -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(

View file

@ -20,7 +20,7 @@ main() {
}
class Foo {
String text;
String? text;
String get foo => 'poo$text';

View file

@ -55,7 +55,9 @@ 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(
@ -65,7 +67,7 @@ RequestHandler validate(Validator validator,
req.bodyAsMap
..clear()
..addAll(result.data);
}
return true;
};
}
@ -75,8 +77,10 @@ RequestHandler validate(Validator validator,
RequestHandler validateQuery(Validator validator,
{String errorMessage = 'Invalid data.'}) {
return (RequestContext req, res) async {
var app = req.app;
if (app != null) {
var result =
await asyncApplyValidator(validator, req.queryParameters, req.app);
await asyncApplyValidator(validator, req.queryParameters, app);
if (result.errors.isNotEmpty) {
throw AngelHttpException.badRequest(
@ -86,7 +90,7 @@ RequestHandler validateQuery(Validator validator,
req.queryParameters
..clear()
..addAll(result.data);
}
return true;
};
}
@ -96,8 +100,9 @@ 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(
@ -107,6 +112,7 @@ HookedServiceEventListener validateEvent(Validator validator,
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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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`.

View file

@ -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');
}

View file

@ -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

View file

@ -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)));
});
}

View file

@ -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);
});

View file

@ -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;
}));
}