From 1c478f0b4aef8232f78fcc80ff97b24d5951bcf4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 27 Jun 2018 22:34:05 -0400 Subject: [PATCH] 1.0.4 --- .analysis-options | 3 --- CHANGELOG.md | 4 ++++ README.md | 5 ++++- analysis_options.yaml | 3 +++ lib/angel_validate.dart | 9 ++++++++- lib/server.dart | 2 +- lib/src/matchers.dart | 3 ++- lib/src/validator.dart | 19 ++++++++++++------- pubspec.yaml | 8 +++++--- test/basic_test.dart | 5 +++++ test/server_test.dart | 34 +++++++++++++++++++++++++--------- validate.iml | 1 + 12 files changed, 70 insertions(+), 26 deletions(-) delete mode 100644 .analysis-options create mode 100644 CHANGELOG.md create mode 100644 analysis_options.yaml diff --git a/.analysis-options b/.analysis-options deleted file mode 100644 index 7b8e6002..00000000 --- a/.analysis-options +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - strong-mode: true - exclude: ./scripts-bin/**/*.dart \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..4566b79b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# 1.0.4 +* `isNonEmptyString` trims strings. +* `ValidationException` extends `AngelHttpException`. +* Added `requireField` and `requireFields`. \ No newline at end of file diff --git a/README.md b/README.md index b9302035..1f1f7933 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,10 @@ to throw an error if it is not present. ```dart main() { var validator = new Validator({ - 'googleId*': isString + 'googleId*': isString, + + // You can also use `requireField` + requireField('googleId'): isString, }); } ``` diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..eae1e42a --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,3 @@ +analyzer: + strong-mode: + implicit-casts: false \ No newline at end of file diff --git a/lib/angel_validate.dart b/lib/angel_validate.dart index a9b29e88..bcb55eea 100644 --- a/lib/angel_validate.dart +++ b/lib/angel_validate.dart @@ -3,4 +3,11 @@ library angel_validate; export 'package:matcher/matcher.dart'; export 'src/matchers.dart'; -export 'src/validator.dart'; \ No newline at end of file +export 'src/validator.dart'; + +/// Marks a field name as required. +String requireField(String field) => '$field*'; + +/// Marks multiple fields as required. +String requireFields(Iterable fields) => + fields.map(requireField).join(', '); diff --git a/lib/server.dart b/lib/server.dart index f0b8f0e5..fa983fc7 100644 --- a/lib/server.dart +++ b/lib/server.dart @@ -88,7 +88,7 @@ RequestMiddleware validateQuery(Validator validator, HookedServiceEventListener validateEvent(Validator validator, {String errorMessage: 'Invalid data.'}) { return (HookedServiceEvent e) { - var result = validator.check(e.data); + var result = validator.check(e.data as Map); if (result.errors.isNotEmpty) { throw new AngelHttpException.badRequest( diff --git a/lib/src/matchers.dart b/lib/src/matchers.dart index 050b85c3..53fff6e2 100644 --- a/lib/src/matchers.dart +++ b/lib/src/matchers.dart @@ -34,7 +34,8 @@ final Matcher isString = predicate((value) => value is String, 'a String'); /// Asserts that a value is a non-empty `String`. final Matcher isNonEmptyString = predicate( - (value) => value is String && value.isNotEmpty, 'a non-empty String'); + (value) => value is String && value.trim().isNotEmpty, + 'a non-empty String'); /// Asserts that a `String` is an `http://` or `https://` URL. /// diff --git a/lib/src/validator.dart b/lib/src/validator.dart index 3e80381b..9ae82a0e 100644 --- a/lib/src/validator.dart +++ b/lib/src/validator.dart @@ -1,3 +1,4 @@ +import 'package:angel_http_exception/angel_http_exception.dart'; import 'package:matcher/matcher.dart'; final RegExp _asterisk = new RegExp(r'\*$'); @@ -19,13 +20,13 @@ Map autoParse(Map inputData, Iterable fields) { for (var key in inputData.keys) { if (!fields.contains(key)) { - data[key] = inputData[key]; + data[key.toString()] = inputData[key]; } else { try { var n = inputData[key] is num ? inputData[key] : num.parse(inputData[key].toString()); - data[key] = n == n.toInt() ? n.toInt() : n; + data[key.toString()] = n == n.toInt() ? n.toInt() : n; } catch (e) { // Invalid number, don't pass it } @@ -38,7 +39,7 @@ Map autoParse(Map inputData, Iterable fields) { /// Removes undesired fields from a `Map`. Map filter(Map inputData, Iterable only) { return inputData.keys.fold({}, (map, key) { - if (only.contains(key)) map[key] = inputData[key]; + if (only.contains(key.toString())) map[key.toString()] = inputData[key]; return map; }); } @@ -153,7 +154,7 @@ class Validator extends Matcher { for (Matcher matcher in rules[key]) { try { if (matcher is Validator) { - var result = matcher.check(value); + var result = matcher.check(value as Map); if (result.errors.isNotEmpty) { errors.addAll(result.errors); @@ -310,7 +311,7 @@ class Validator extends Matcher { @override bool matches(item, Map matchState) { - enforce(item); + enforce(item as Map); return true; } @@ -333,14 +334,18 @@ class ValidationResult { } /// Occurs when user-provided data is invalid. -class ValidationException { +class ValidationException extends AngelHttpException { /// A list of errors that resulted in the given data being marked invalid. final List errors = []; /// A descriptive message describing the error. final String message; - ValidationException(this.message, {List errors: const []}) { + ValidationException(this.message, {List errors: const []}) + : super(new FormatException(message), + statusCode: 400, + errors: errors ?? [], + stackTrace: StackTrace.current) { if (errors != null) this.errors.addAll(errors); } diff --git a/pubspec.yaml b/pubspec.yaml index cebf2eea..d65fd2cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,17 @@ name: angel_validate description: Cross-platform validation library based on `matcher`. -version: 1.0.3 +version: 1.0.4 author: Tobe O homepage: https://github.com/angel-dart/validate environment: - sdk: ">=1.19.0" + sdk: ">=1.19.0 <3.0.0" dependencies: angel_framework: ^1.0.0-dev + angel_http_exception: ^1.0.0 matcher: ^0.12.0 dev_dependencies: - angel_diagnostics: ^1.0.0-dev angel_test: ^1.0.0-dev browser: ^0.10.0 + dart2_constant: ^1.0.0 + logging: ^0.11.0 test: ^0.12.18 \ No newline at end of file diff --git a/test/basic_test.dart b/test/basic_test.dart index 49506a69..3bc2b6ed 100644 --- a/test/basic_test.dart +++ b/test/basic_test.dart @@ -22,6 +22,11 @@ main() { expect(result.errors.first, equals('Hello, world!')); }); + test('requireField', () => expect(requireField('foo'), 'foo*')); + + test('requireFields', + () => expect(requireFields(['foo', 'bar']), 'foo*, bar*')); + test('todo', () { expect(() { todoSchema diff --git a/test/server_test.dart b/test/server_test.dart index 15d24190..0aa39608 100644 --- a/test/server_test.dart +++ b/test/server_test.dart @@ -1,30 +1,40 @@ -import 'dart:io'; -import 'package:angel_diagnostics/angel_diagnostics.dart'; import 'package:angel_framework/angel_framework.dart'; import 'package:angel_test/angel_test.dart'; import 'package:angel_validate/server.dart'; +import 'package:dart2_constant/convert.dart'; +import 'package:logging/logging.dart'; +import 'package:mock_request/mock_request.dart'; import 'package:test/test.dart'; final Validator echoSchema = new Validator({'message*': isString}); +void printRecord(LogRecord rec) { + print(rec); + if (rec.error != null) print(rec.error); + if (rec.stackTrace != null) print(rec.stackTrace); +} + main() { Angel app; + AngelHttp http; TestClient client; setUp(() async { app = new Angel(); + http = new AngelHttp(app, useZone: false); app.chain(validate(echoSchema)).post('/echo', (RequestContext req, res) async { res.write('Hello, ${req.body['message']}!'); }); - await app.configure(logRequests(new File('log.txt'))); + app.logger = new Logger('angel')..onRecord.listen(printRecord); client = await connectTo(app); }); tearDown(() async { await client.close(); + await http.close(); app = null; client = null; }); @@ -32,17 +42,23 @@ main() { group('echo', () { test('validate', () async { var response = await client.post('/echo', - body: {'message': 'world'}, headers: {HttpHeaders.ACCEPT: '*/*'}); + body: {'message': 'world'}, headers: {'accept': '*/*'}); print('Response: ${response.body}'); - expect(response, hasStatus(HttpStatus.OK)); + expect(response, hasStatus(200)); expect(response.body, equals('Hello, world!')); }); test('enforce', () async { - var response = await client.post('/echo', - body: {'foo': 'bar'}, headers: {HttpHeaders.ACCEPT: '*/*'}); - print('Response: ${response.body}'); - expect(response, hasStatus(HttpStatus.BAD_REQUEST)); + var rq = new MockHttpRequest('POST', new Uri(path: '/echo')) + ..headers.add('accept', '*/*') + ..headers.add('content-type', 'application/json') + ..write(json.encode({'foo': 'bar'})) + ..close(); + http.handleRequest(rq); + + var responseBody = await rq.response.transform(utf8.decoder).join(); + print('Response: ${responseBody}'); + expect(rq.response.statusCode, 400); }); }); } diff --git a/validate.iml b/validate.iml index 5a5ced28..0854fb6e 100644 --- a/validate.iml +++ b/validate.iml @@ -3,6 +3,7 @@ +