platform/packages/validate/lib/server.dart

151 lines
4.2 KiB
Dart
Raw Normal View History

2021-02-21 02:47:23 +00:00
/// Support for using `angel_validate` with the Angel Framework.
2021-05-14 11:47:44 +00:00
library angel3_validate.server;
2021-02-21 02:47:23 +00:00
import 'dart:async';
2021-05-14 11:47:44 +00:00
import 'package:angel3_framework/angel3_framework.dart';
2021-02-21 02:47:23 +00:00
import 'src/async.dart';
2021-05-14 11:47:44 +00:00
import 'angel3_validate.dart';
2021-02-21 02:47:23 +00:00
export 'src/async.dart';
2021-05-14 11:47:44 +00:00
export 'angel3_validate.dart';
2021-02-21 02:47:23 +00:00
/// Auto-parses numbers in `req.bodyAsMap`.
RequestHandler autoParseBody(List<String> fields) {
return (RequestContext req, res) async {
await req.parseBody();
req.bodyAsMap.addAll(autoParse(req.bodyAsMap, fields));
return true;
};
}
/// Auto-parses numbers in `req.queryParameters`.
RequestHandler autoParseQuery(List<String> fields) {
return (RequestContext req, res) async {
req.queryParameters.addAll(autoParse(req.queryParameters, fields));
return true;
};
}
/// Filters unwanted data out of `req.bodyAsMap`.
RequestHandler filterBody(Iterable<String> only) {
return (RequestContext req, res) async {
await req.parseBody();
var filtered = filter(req.bodyAsMap, only);
req.bodyAsMap
..clear()
..addAll(filtered);
return true;
};
}
/// Filters unwanted data out of `req.queryParameters`.
RequestHandler filterQuery(Iterable<String> only) {
return (RequestContext req, res) async {
var filtered = filter(req.queryParameters, only);
req.queryParameters
..clear()
..addAll(filtered);
return true;
};
}
/// Validates the data in `req.bodyAsMap`, and sets the body to
/// filtered data before continuing the response.
RequestHandler validate(Validator validator,
{String errorMessage = 'Invalid data.'}) {
return (RequestContext req, res) async {
await req.parseBody();
2021-04-10 12:42:55 +00:00
var app = req.app;
if (app != null) {
var result = await asyncApplyValidator(validator, req.bodyAsMap, app);
2021-02-21 02:47:23 +00:00
2021-04-10 12:42:55 +00:00
if (result.errors.isNotEmpty) {
throw AngelHttpException.badRequest(
message: errorMessage, errors: result.errors);
}
2021-02-21 02:47:23 +00:00
2021-04-10 12:42:55 +00:00
req.bodyAsMap
..clear()
..addAll(result.data);
}
2021-02-21 02:47:23 +00:00
return true;
};
}
/// Validates the data in `req.queryParameters`, and sets the query to
/// filtered data before continuing the response.
RequestHandler validateQuery(Validator validator,
{String errorMessage = 'Invalid data.'}) {
return (RequestContext req, res) async {
2021-04-10 12:42:55 +00:00
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);
}
2021-02-21 02:47:23 +00:00
2021-04-10 12:42:55 +00:00
req.queryParameters
..clear()
..addAll(result.data);
2021-02-21 02:47:23 +00:00
}
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 {
2021-04-10 12:42:55 +00:00
var app = e.request?.app ?? e.service.app;
if (app != null) {
var result = await asyncApplyValidator(validator, e.data as Map, app);
2021-02-21 02:47:23 +00:00
2021-04-10 12:42:55 +00:00
if (result.errors.isNotEmpty) {
throw AngelHttpException.badRequest(
message: errorMessage, errors: result.errors);
}
2021-02-21 02:47:23 +00:00
2021-04-10 12:42:55 +00:00
e.data
..clear()
..addAll(result.data);
}
2021-02-21 02:47:23 +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 = StringDescription("'$key': expected ");
2021-04-10 12:42:55 +00:00
for (var rule in validator.rules[key]!) {
2021-02-21 02:47:23 +00:00
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 = Map<String, dynamic>.from(result.data);
for (var key in errantKeys) {
m.remove(key);
}
return result.withData(m).withErrors(errors);
}