/// Support for using `angel_validate` with the Angel Framework. library angel_validate.server; import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; import 'src/async.dart'; import 'angel_validate.dart'; export 'src/async.dart'; export 'angel_validate.dart'; /// Auto-parses numbers in `req.body`. RequestHandler autoParseBody(List fields) { return (RequestContext req, res) async { (await req.parseBody()).addAll(autoParse(await req.parseBody(), fields)); return true; }; } /// Auto-parses numbers in `req.query`. RequestHandler autoParseQuery(List fields) { return (RequestContext req, res) async { (await req.parseQuery()).addAll(autoParse(await req.parseQuery(), fields)); return true; }; } /// Filters unwanted data out of `req.body`. RequestHandler filterBody(Iterable only) { return (RequestContext req, res) async { var filtered = filter(await req.parseBody(), only); (await req.parseBody()) ..clear() ..addAll(filtered); return true; }; } /// Filters unwanted data out of `req.query`. RequestHandler filterQuery(Iterable only) { return (RequestContext req, res) async { var query = await req.parseQuery(); var filtered = filter(query, only); query ..clear() ..addAll(filtered); return true; }; } /// Validates the data in `req.body`, and sets the body to /// filtered data before continuing the response. RequestHandler validate(Validator validator, {String errorMessage: 'Invalid data.'}) { return (RequestContext req, res) async { var result = await asyncApplyValidator(validator, await req.parseBody(), req.app); if (result.errors.isNotEmpty) { throw new AngelHttpException.badRequest( message: errorMessage, errors: result.errors); } (await req.parseBody()) ..clear() ..addAll(result.data); return true; }; } /// Validates the data in `req.query`, and sets the query to /// filtered data before continuing the response. RequestHandler validateQuery(Validator validator, {String errorMessage: 'Invalid data.'}) { return (RequestContext req, res) async { var result = await asyncApplyValidator(validator, await req.parseQuery(), req.app); if (result.errors.isNotEmpty) { throw new AngelHttpException.badRequest( message: errorMessage, errors: result.errors); } (await req.parseQuery()) ..clear() ..addAll(result.data); return true; }; } /// Validates the data in `e.data`, and sets the data to /// filtered data before continuing the service event. 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)); if (result.errors.isNotEmpty) { throw new AngelHttpException.badRequest( message: errorMessage, errors: result.errors); } e.data ..clear() ..addAll(result.data); }; } /// Asynchronously apply a [validator], running any [AngelMatcher]s. Future asyncApplyValidator( Validator validator, Map data, Angel app) async { var result = validator.check(data); if (result.errors.isNotEmpty) return result; var errantKeys = [], errors = []; for (var key in result.data.keys) { var value = result.data[key]; var description = new StringDescription("'$key': expected "); for (var rule in validator.rules[key]) { if (rule is AngelMatcher) { var r = await rule.matchesWithAngel(value, key, result.data, {}, app); if (!r) { errors.add(rule.describe(description).toString().trim()); errantKeys.add(key); break; } } } } var m = new Map.from(result.data); for (var key in errantKeys) { m.remove(key); } return result.withData(m).withErrors(errors); }