From 1ed5d20ad62f7927b0894c602166754f61b3f1c4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Mon, 4 May 2020 12:54:48 -0400 Subject: [PATCH] validate: Add MapField, Field.deserialize, Field.decode --- packages/validate/example/main.dart | 10 +++++- packages/validate/lib/src/common_fields.dart | 14 ++++----- packages/validate/lib/src/field.dart | 32 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/packages/validate/example/main.dart b/packages/validate/example/main.dart index 858734ad..9a54f3ab 100644 --- a/packages/validate/example/main.dart +++ b/packages/validate/example/main.dart @@ -72,6 +72,14 @@ Future main() async { '''); }); + + app.post('/b', (req, res) async { + var form = Form(fields: [ + MapField('todo', todoForm), + ]); + var data = await form.read(req); + return data; + }); app.fallback((req, res) => throw AngelHttpException.notFound()); @@ -82,7 +90,7 @@ Future main() async { } }; - await http.startServer('127.0.0.1', 3000); + await http.startServer('127.0.0.1', 3011); print('Listening at ${http.uri}'); } diff --git a/packages/validate/lib/src/common_fields.dart b/packages/validate/lib/src/common_fields.dart index fc58662c..fa1a3ed6 100644 --- a/packages/validate/lib/src/common_fields.dart +++ b/packages/validate/lib/src/common_fields.dart @@ -353,12 +353,12 @@ class ImageField extends Field { } } +/// A [Field] implementation that reads structured data by decoding it with a [Form]. class MapField extends Field> { - @override - final String name; + /// The underlying [Form] that specifies the types of data this object must contained. final Form form; - MapField(this.name, this.form, {String label, bool isRequired = true}) + MapField(String name, this.form, {String label, bool isRequired = true}) : super(name, 'text', label: label, isRequired: isRequired); @override @@ -369,17 +369,17 @@ class MapField extends Field> { FutureOr>> read( Map fields, Iterable files) { var value = fields[name]; - Map mapValue; + 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; + mapValue = value.cast(); } else if (value is String) { var decoded = json.decode(value); if (decoded is Map) { - mapValue = decoded; + mapValue = decoded.cast(); } else { return FieldReadResult.failure([ 'If "$name" is given as a string, then it must produce a map/dict/object when decoded as JSON.' @@ -390,6 +390,6 @@ class MapField extends Field> { ['"$name" must be either a map/dict/object, or a string.']); } - + return form.readFromMap(mapValue); } } diff --git a/packages/validate/lib/src/field.dart b/packages/validate/lib/src/field.dart index 743eb192..6d6eb8ea 100644 --- a/packages/validate/lib/src/field.dart +++ b/packages/validate/lib/src/field.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:angel_framework/angel_framework.dart'; import 'package:matcher/matcher.dart'; import 'form.dart'; @@ -57,6 +58,14 @@ abstract class Field { /// [matchers] fails. Field match(Iterable matchers) => _MatchedField(this, matchers); + /// Wraps this instance in one that calls the [converter] to deserialize + /// the value into another type. + Field deserialize(FutureOr Function(T) converter) => + _DeserializeField(this, converter); + + /// Same as [deserialize], but uses a [codec] to deserialize data. + Field decode(Codec codec) => deserialize(codec.decode); + /// Calls [read], and returns the retrieve value from the body. /// /// If [query] is `true` (default: `false`), then the value will @@ -124,3 +133,26 @@ class _MatchedField extends Field { } } } + +class _DeserializeField extends Field { + final Field inner; + final FutureOr Function(T) converter; + + _DeserializeField(this.inner, this.converter) + : super(inner.name, inner.type, + label: inner.label, isRequired: inner.isRequired); + + @override + FutureOr accept(FormRenderer renderer) => inner.accept(renderer); + + @override + FutureOr> read( + Map fields, Iterable files) async { + var result = await inner.read(fields, files); + if (!result.isSuccess) { + return FieldReadResult.failure(result.errors); + } else { + return FieldReadResult.success(await converter(result.value)); + } + } +}