/// 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`. RequestMiddleware autoParseBody(List fields) { return (RequestContext req, res) async { (await req.lazyBody()).addAll(autoParse(req.body, fields)); return true; }; } /// Auto-parses numbers in `req.query`. RequestMiddleware autoParseQuery(List fields) { return (RequestContext req, res) async { req.query.addAll(autoParse(req.query, fields)); return true; }; } /// Filters unwanted data out of `req.body`. RequestMiddleware filterBody(Iterable only) { return (RequestContext req, res) async { var filtered = filter(await req.lazyBody(), only); req.body ..clear() ..addAll(filtered); return true; }; } /// Filters unwanted data out of `req.query`. RequestMiddleware filterQuery(Iterable only) { return (RequestContext req, res) async { var filtered = filter(req.query, only); req.query ..clear() ..addAll(filtered); return true; }; } /// Validates the data in `req.body`, and sets the body to /// filtered data before continuing the response. RequestMiddleware validate(Validator validator, {String errorMessage: 'Invalid data.'}) { return (RequestContext req, res) async { var result = await asyncApplyValidator(validator, await req.lazyBody(), req.app); if (result.errors.isNotEmpty) { throw new AngelHttpException.badRequest( message: errorMessage, errors: result.errors); } req.body ..clear() ..addAll(result.data); return true; }; } /// Validates the data in `req.query`, and sets the query to /// filtered data before continuing the response. RequestMiddleware validateQuery(Validator validator, {String errorMessage: 'Invalid data.'}) { return (RequestContext req, res) async { var result = await asyncApplyValidator(validator, req.query, req.app); if (result.errors.isNotEmpty) { throw new AngelHttpException.badRequest( message: errorMessage, errors: result.errors); } req.query ..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) as Angel); 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); }