validate: Rework Field.read to omit RequestContext
This commit is contained in:
parent
76fe71a474
commit
104b8da314
4 changed files with 93 additions and 19 deletions
|
@ -1,8 +1,10 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:image/image.dart';
|
||||
import 'field.dart';
|
||||
import 'form.dart';
|
||||
import 'form_renderer.dart';
|
||||
|
||||
/// A [Field] that accepts plain text.
|
||||
|
@ -38,7 +40,7 @@ class TextField extends Field<String> {
|
|||
}
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<String>> read(RequestContext req,
|
||||
FutureOr<FieldReadResult<String>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
var value = _normalize(fields[name] as String);
|
||||
if (value == null) {
|
||||
|
@ -71,7 +73,7 @@ class BoolField extends Field<bool> {
|
|||
renderer.visitBoolField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<bool>> read(RequestContext req,
|
||||
FutureOr<FieldReadResult<bool>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
if (fields.containsKey(name)) {
|
||||
return FieldReadResult.success(true);
|
||||
|
@ -108,9 +110,9 @@ class NumField<T extends num> extends Field<T> {
|
|||
renderer.visitNumField(this);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<T>> read(RequestContext req,
|
||||
Future<FieldReadResult<T>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await _textField.read(req, fields, files);
|
||||
var result = await _textField.read(fields, files);
|
||||
if (result == null) {
|
||||
return null;
|
||||
} else if (result.isSuccess != true) {
|
||||
|
@ -151,9 +153,9 @@ class DoubleField extends NumField<double> {
|
|||
max: max);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<double>> read(RequestContext req,
|
||||
Future<FieldReadResult<double>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await super.read(req, fields, files);
|
||||
var result = await super.read(fields, files);
|
||||
if (result == null) {
|
||||
return null;
|
||||
} else if (!result.isSuccess) {
|
||||
|
@ -183,9 +185,9 @@ class IntField extends NumField<int> {
|
|||
max: max);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<int>> read(RequestContext req,
|
||||
Future<FieldReadResult<int>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await super.read(req, fields, files);
|
||||
var result = await super.read(fields, files);
|
||||
if (result == null) {
|
||||
return null;
|
||||
} else if (!result.isSuccess) {
|
||||
|
@ -228,9 +230,9 @@ class DateTimeField extends Field<DateTime> {
|
|||
renderer.visitDateTimeField(this);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<DateTime>> read(RequestContext req,
|
||||
Future<FieldReadResult<DateTime>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await _textField.read(req, fields, files);
|
||||
var result = await _textField.read(fields, files);
|
||||
if (result == null) {
|
||||
return null;
|
||||
} else if (result.isSuccess != true) {
|
||||
|
@ -274,7 +276,7 @@ class FileField extends Field<UploadedFile> {
|
|||
renderer.visitFileField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<UploadedFile>> read(RequestContext req,
|
||||
FutureOr<FieldReadResult<UploadedFile>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
var file = files.firstWhere((f) => f.name == name, orElse: () => null);
|
||||
if (file == null) {
|
||||
|
@ -328,9 +330,9 @@ class ImageField extends Field<Image> {
|
|||
renderer.visitImageField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<Image>> read(RequestContext req,
|
||||
FutureOr<FieldReadResult<Image>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await fileField.read(req, fields, files);
|
||||
var result = await fileField.read(fields, files);
|
||||
if (result == null) {
|
||||
return null;
|
||||
} else if (!result.isSuccess) {
|
||||
|
@ -350,3 +352,44 @@ class ImageField extends Field<Image> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MapField extends Field<Map<String, dynamic>> {
|
||||
@override
|
||||
final String name;
|
||||
final Form form;
|
||||
|
||||
MapField(this.name, this.form, {String label, bool isRequired = true})
|
||||
: super(name, 'text', label: label, isRequired: isRequired);
|
||||
|
||||
@override
|
||||
FutureOr<U> accept<U>(FormRenderer<U> renderer) =>
|
||||
renderer.visitMapField(this);
|
||||
|
||||
@override
|
||||
FutureOr<FieldReadResult<Map<String, dynamic>>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) {
|
||||
var value = fields[name];
|
||||
Map mapValue;
|
||||
|
||||
// Value can be either a Map, or a JSON-encoded Map.
|
||||
if (value == null) {
|
||||
return null;
|
||||
} else if (value is Map) {
|
||||
mapValue = value;
|
||||
} else if (value is String) {
|
||||
var decoded = json.decode(value);
|
||||
if (decoded is Map) {
|
||||
mapValue = decoded;
|
||||
} else {
|
||||
return FieldReadResult.failure([
|
||||
'If "$name" is given as a string, then it must produce a map/dict/object when decoded as JSON.'
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
return FieldReadResult.failure(
|
||||
['"$name" must be either a map/dict/object, or a string.']);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ abstract class Field<T> {
|
|||
///
|
||||
/// If it returns `null` and [isRequired] is `true`, an error must
|
||||
/// be generated.
|
||||
FutureOr<FieldReadResult<T>> read(RequestContext req,
|
||||
FutureOr<FieldReadResult<T>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files);
|
||||
|
||||
/// Accepts a form renderer.
|
||||
|
@ -72,8 +72,8 @@ abstract class Field<T> {
|
|||
await req.parseBody();
|
||||
uploadedFiles = req.uploadedFiles;
|
||||
}
|
||||
var result = await read(
|
||||
req, query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
||||
var result =
|
||||
await read(query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
||||
if (result?.isSuccess != true && defaultValue != null) {
|
||||
return defaultValue;
|
||||
} else if (result == null) {
|
||||
|
@ -103,9 +103,9 @@ class _MatchedField<T> extends Field<T> {
|
|||
FutureOr<U> accept<U>(FormRenderer<U> renderer) => inner.accept(renderer);
|
||||
|
||||
@override
|
||||
Future<FieldReadResult<T>> read(RequestContext req,
|
||||
Future<FieldReadResult<T>> read(
|
||||
Map<String, dynamic> fields, Iterable<UploadedFile> files) async {
|
||||
var result = await inner.read(req, fields, files);
|
||||
var result = await inner.read(fields, files);
|
||||
if (!result.isSuccess) {
|
||||
return result;
|
||||
} else {
|
||||
|
|
|
@ -109,7 +109,36 @@ class Form {
|
|||
|
||||
for (var field in fields) {
|
||||
var result = await field.read(
|
||||
req, query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
||||
query ? req.queryParameters : req.bodyAsMap, uploadedFiles);
|
||||
if (result == null && field.isRequired) {
|
||||
errors.add(reportMissingField(field.name, query: query));
|
||||
} else if (!result.isSuccess) {
|
||||
errors.addAll(result.errors);
|
||||
} else {
|
||||
out[field.name] = result.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.isNotEmpty) {
|
||||
return FieldReadResult.failure(errors);
|
||||
} else {
|
||||
return FieldReadResult.success(out);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [read], but reads data directly from a [map].
|
||||
///
|
||||
/// If [query] is `true`, resulting error messages will be generated as though
|
||||
/// the field were being read from a map of query parameters.
|
||||
Future<FieldReadResult<Map<String, dynamic>>> readFromMap(
|
||||
Map<String, dynamic> map,
|
||||
{bool query = false}) async {
|
||||
var out = <String, dynamic>{};
|
||||
var errors = <String>[];
|
||||
var uploadedFiles = <UploadedFile>[];
|
||||
|
||||
for (var field in fields) {
|
||||
var result = await field.read(map, uploadedFiles);
|
||||
if (result == null && field.isRequired) {
|
||||
errors.add(reportMissingField(field.name, query: query));
|
||||
} else if (!result.isSuccess) {
|
||||
|
|
|
@ -18,4 +18,6 @@ abstract class FormRenderer<T> {
|
|||
FutureOr<T> visitNumField(NumField field);
|
||||
|
||||
FutureOr<T> visitTextField(TextField field);
|
||||
|
||||
FutureOr<T> visitMapField(MapField field);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue