platform/lib/server.dart

147 lines
4 KiB
Dart
Raw Normal View History

2016-12-25 21:26:55 +00:00
/// Support for using `angel_validate` with the Angel Framework.
library angel_validate.server;
2018-06-28 16:34:05 +00:00
import 'dart:async';
2016-12-25 21:26:55 +00:00
import 'package:angel_framework/angel_framework.dart';
2018-06-28 15:07:04 +00:00
import 'src/async.dart';
2016-12-25 21:26:55 +00:00
import 'angel_validate.dart';
2018-06-28 15:07:04 +00:00
export 'src/async.dart';
2016-12-25 21:26:55 +00:00
export 'angel_validate.dart';
2016-12-26 13:04:42 +00:00
/// Auto-parses numbers in `req.body`.
2018-08-26 22:34:51 +00:00
RequestHandler autoParseBody(List<String> fields) {
2016-12-26 13:04:42 +00:00
return (RequestContext req, res) async {
2018-11-06 19:18:48 +00:00
var body = await req.parseBody();
body.addAll(autoParse(body, fields));
2016-12-26 13:04:42 +00:00
return true;
};
}
/// Auto-parses numbers in `req.query`.
2018-08-26 22:34:51 +00:00
RequestHandler autoParseQuery(List<String> fields) {
2016-12-26 13:04:42 +00:00
return (RequestContext req, res) async {
2018-11-06 19:18:48 +00:00
var query = new Map<String, dynamic>.from(await req.parseQuery());
(await req.parseQuery()).addAll(autoParse(query, fields));
2016-12-26 13:04:42 +00:00
return true;
};
}
2017-01-25 23:03:45 +00:00
/// Filters unwanted data out of `req.body`.
2018-08-26 22:34:51 +00:00
RequestHandler filterBody(Iterable<String> only) {
2017-01-25 23:03:45 +00:00
return (RequestContext req, res) async {
2018-11-06 19:18:48 +00:00
var body = await req.parseBody();
var filtered = filter(body, only);
body
2017-01-25 23:03:45 +00:00
..clear()
..addAll(filtered);
return true;
};
}
/// Filters unwanted data out of `req.query`.
2018-08-26 22:34:51 +00:00
RequestHandler filterQuery(Iterable<String> only) {
2017-01-25 23:03:45 +00:00
return (RequestContext req, res) async {
2018-11-02 00:55:28 +00:00
var query = await req.parseQuery();
var filtered = filter(query, only);
2018-11-06 19:18:48 +00:00
(await req.parseQuery())
2017-01-25 23:03:45 +00:00
..clear()
..addAll(filtered);
return true;
};
}
2016-12-25 21:26:55 +00:00
/// Validates the data in `req.body`, and sets the body to
/// filtered data before continuing the response.
2018-08-26 22:34:51 +00:00
RequestHandler validate(Validator validator,
2016-12-25 22:46:08 +00:00
{String errorMessage: 'Invalid data.'}) {
return (RequestContext req, res) async {
2018-11-06 19:18:48 +00:00
var body = await req.parseBody();
2018-11-06 19:19:07 +00:00
var result = await asyncApplyValidator(validator, body, req.app);
2016-12-25 21:26:55 +00:00
2016-12-25 22:46:08 +00:00
if (result.errors.isNotEmpty) {
2017-03-29 02:56:51 +00:00
throw new AngelHttpException.badRequest(
2016-12-25 22:46:08 +00:00
message: errorMessage, errors: result.errors);
}
2018-11-06 19:18:48 +00:00
body
2016-12-25 22:46:08 +00:00
..clear()
..addAll(result.data);
return true;
};
2016-12-25 21:26:55 +00:00
}
2016-12-25 22:46:08 +00:00
/// Validates the data in `req.query`, and sets the query to
2016-12-25 21:26:55 +00:00
/// filtered data before continuing the response.
2018-08-26 22:34:51 +00:00
RequestHandler validateQuery(Validator validator,
2016-12-25 22:46:08 +00:00
{String errorMessage: 'Invalid data.'}) {
return (RequestContext req, res) async {
2018-11-06 19:18:48 +00:00
var query = await req.parseQuery();
2018-11-06 19:19:07 +00:00
var result = await asyncApplyValidator(validator, query, req.app);
2016-12-25 22:46:08 +00:00
if (result.errors.isNotEmpty) {
2017-03-29 02:56:51 +00:00
throw new AngelHttpException.badRequest(
2016-12-25 22:46:08 +00:00
message: errorMessage, errors: result.errors);
}
2016-12-25 21:26:55 +00:00
2018-11-02 00:55:28 +00:00
(await req.parseQuery())
2016-12-25 22:46:08 +00:00
..clear()
..addAll(result.data);
return true;
};
2016-12-25 21:26:55 +00:00
}
/// Validates the data in `e.data`, and sets the data to
/// filtered data before continuing the service event.
2016-12-25 22:46:08 +00:00
HookedServiceEventListener validateEvent(Validator validator,
{String errorMessage: 'Invalid data.'}) {
2018-06-28 16:34:05 +00:00
return (HookedServiceEvent e) async {
var result = await asyncApplyValidator(
2018-08-26 22:34:51 +00:00
validator, e.data as Map, (e.request?.app ?? e.service.app));
2016-12-25 21:26:55 +00:00
2016-12-25 22:46:08 +00:00
if (result.errors.isNotEmpty) {
2017-03-29 02:56:51 +00:00
throw new AngelHttpException.badRequest(
2016-12-25 22:46:08 +00:00
message: errorMessage, errors: result.errors);
}
2016-12-25 21:26:55 +00:00
2016-12-25 22:46:08 +00:00
e.data
..clear()
..addAll(result.data);
};
}
2018-06-28 16:34:05 +00:00
/// Asynchronously apply a [validator], running any [AngelMatcher]s.
Future<ValidationResult> asyncApplyValidator(
Validator validator, Map data, Angel app) async {
var result = validator.check(data);
if (result.errors.isNotEmpty) return result;
var errantKeys = <String>[], errors = <String>[];
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<String, dynamic>.from(result.data);
for (var key in errantKeys) {
m.remove(key);
}
return result.withData(m).withErrors(errors);
}