1.0.4
This commit is contained in:
parent
53c9de3d5e
commit
1c478f0b4a
12 changed files with 70 additions and 26 deletions
|
@ -1,3 +0,0 @@
|
||||||
analyzer:
|
|
||||||
strong-mode: true
|
|
||||||
exclude: ./scripts-bin/**/*.dart
|
|
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# 1.0.4
|
||||||
|
* `isNonEmptyString` trims strings.
|
||||||
|
* `ValidationException` extends `AngelHttpException`.
|
||||||
|
* Added `requireField` and `requireFields`.
|
|
@ -92,7 +92,10 @@ to throw an error if it is not present.
|
||||||
```dart
|
```dart
|
||||||
main() {
|
main() {
|
||||||
var validator = new Validator({
|
var validator = new Validator({
|
||||||
'googleId*': isString
|
'googleId*': isString,
|
||||||
|
|
||||||
|
// You can also use `requireField`
|
||||||
|
requireField('googleId'): isString,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
3
analysis_options.yaml
Normal file
3
analysis_options.yaml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
analyzer:
|
||||||
|
strong-mode:
|
||||||
|
implicit-casts: false
|
|
@ -4,3 +4,10 @@ library angel_validate;
|
||||||
export 'package:matcher/matcher.dart';
|
export 'package:matcher/matcher.dart';
|
||||||
export 'src/matchers.dart';
|
export 'src/matchers.dart';
|
||||||
export 'src/validator.dart';
|
export 'src/validator.dart';
|
||||||
|
|
||||||
|
/// Marks a field name as required.
|
||||||
|
String requireField(String field) => '$field*';
|
||||||
|
|
||||||
|
/// Marks multiple fields as required.
|
||||||
|
String requireFields(Iterable<String> fields) =>
|
||||||
|
fields.map(requireField).join(', ');
|
||||||
|
|
|
@ -88,7 +88,7 @@ RequestMiddleware validateQuery(Validator validator,
|
||||||
HookedServiceEventListener validateEvent(Validator validator,
|
HookedServiceEventListener validateEvent(Validator validator,
|
||||||
{String errorMessage: 'Invalid data.'}) {
|
{String errorMessage: 'Invalid data.'}) {
|
||||||
return (HookedServiceEvent e) {
|
return (HookedServiceEvent e) {
|
||||||
var result = validator.check(e.data);
|
var result = validator.check(e.data as Map);
|
||||||
|
|
||||||
if (result.errors.isNotEmpty) {
|
if (result.errors.isNotEmpty) {
|
||||||
throw new AngelHttpException.badRequest(
|
throw new AngelHttpException.badRequest(
|
||||||
|
|
|
@ -34,7 +34,8 @@ final Matcher isString = predicate((value) => value is String, 'a String');
|
||||||
|
|
||||||
/// Asserts that a value is a non-empty `String`.
|
/// Asserts that a value is a non-empty `String`.
|
||||||
final Matcher isNonEmptyString = predicate(
|
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.
|
/// Asserts that a `String` is an `http://` or `https://` URL.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:angel_http_exception/angel_http_exception.dart';
|
||||||
import 'package:matcher/matcher.dart';
|
import 'package:matcher/matcher.dart';
|
||||||
|
|
||||||
final RegExp _asterisk = new RegExp(r'\*$');
|
final RegExp _asterisk = new RegExp(r'\*$');
|
||||||
|
@ -19,13 +20,13 @@ Map<String, dynamic> autoParse(Map inputData, Iterable<String> fields) {
|
||||||
|
|
||||||
for (var key in inputData.keys) {
|
for (var key in inputData.keys) {
|
||||||
if (!fields.contains(key)) {
|
if (!fields.contains(key)) {
|
||||||
data[key] = inputData[key];
|
data[key.toString()] = inputData[key];
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
var n = inputData[key] is num
|
var n = inputData[key] is num
|
||||||
? inputData[key]
|
? inputData[key]
|
||||||
: num.parse(inputData[key].toString());
|
: num.parse(inputData[key].toString());
|
||||||
data[key] = n == n.toInt() ? n.toInt() : n;
|
data[key.toString()] = n == n.toInt() ? n.toInt() : n;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Invalid number, don't pass it
|
// Invalid number, don't pass it
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ Map<String, dynamic> autoParse(Map inputData, Iterable<String> fields) {
|
||||||
/// Removes undesired fields from a `Map`.
|
/// Removes undesired fields from a `Map`.
|
||||||
Map<String, dynamic> filter(Map inputData, Iterable<String> only) {
|
Map<String, dynamic> filter(Map inputData, Iterable<String> only) {
|
||||||
return inputData.keys.fold(<String, dynamic>{}, (map, key) {
|
return inputData.keys.fold(<String, dynamic>{}, (map, key) {
|
||||||
if (only.contains(key)) map[key] = inputData[key];
|
if (only.contains(key.toString())) map[key.toString()] = inputData[key];
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -153,7 +154,7 @@ class Validator extends Matcher {
|
||||||
for (Matcher matcher in rules[key]) {
|
for (Matcher matcher in rules[key]) {
|
||||||
try {
|
try {
|
||||||
if (matcher is Validator) {
|
if (matcher is Validator) {
|
||||||
var result = matcher.check(value);
|
var result = matcher.check(value as Map);
|
||||||
|
|
||||||
if (result.errors.isNotEmpty) {
|
if (result.errors.isNotEmpty) {
|
||||||
errors.addAll(result.errors);
|
errors.addAll(result.errors);
|
||||||
|
@ -310,7 +311,7 @@ class Validator extends Matcher {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool matches(item, Map matchState) {
|
bool matches(item, Map matchState) {
|
||||||
enforce(item);
|
enforce(item as Map);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,14 +334,18 @@ class ValidationResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Occurs when user-provided data is invalid.
|
/// 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.
|
/// A list of errors that resulted in the given data being marked invalid.
|
||||||
final List<String> errors = [];
|
final List<String> errors = [];
|
||||||
|
|
||||||
/// A descriptive message describing the error.
|
/// A descriptive message describing the error.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
ValidationException(this.message, {List<String> errors: const []}) {
|
ValidationException(this.message, {List<String> errors: const []})
|
||||||
|
: super(new FormatException(message),
|
||||||
|
statusCode: 400,
|
||||||
|
errors: errors ?? [],
|
||||||
|
stackTrace: StackTrace.current) {
|
||||||
if (errors != null) this.errors.addAll(errors);
|
if (errors != null) this.errors.addAll(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
name: angel_validate
|
name: angel_validate
|
||||||
description: Cross-platform validation library based on `matcher`.
|
description: Cross-platform validation library based on `matcher`.
|
||||||
version: 1.0.3
|
version: 1.0.4
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/validate
|
homepage: https://github.com/angel-dart/validate
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0 <3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^1.0.0-dev
|
angel_framework: ^1.0.0-dev
|
||||||
|
angel_http_exception: ^1.0.0
|
||||||
matcher: ^0.12.0
|
matcher: ^0.12.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel_diagnostics: ^1.0.0-dev
|
|
||||||
angel_test: ^1.0.0-dev
|
angel_test: ^1.0.0-dev
|
||||||
browser: ^0.10.0
|
browser: ^0.10.0
|
||||||
|
dart2_constant: ^1.0.0
|
||||||
|
logging: ^0.11.0
|
||||||
test: ^0.12.18
|
test: ^0.12.18
|
|
@ -22,6 +22,11 @@ main() {
|
||||||
expect(result.errors.first, equals('Hello, world!'));
|
expect(result.errors.first, equals('Hello, world!'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('requireField', () => expect(requireField('foo'), 'foo*'));
|
||||||
|
|
||||||
|
test('requireFields',
|
||||||
|
() => expect(requireFields(['foo', 'bar']), 'foo*, bar*'));
|
||||||
|
|
||||||
test('todo', () {
|
test('todo', () {
|
||||||
expect(() {
|
expect(() {
|
||||||
todoSchema
|
todoSchema
|
||||||
|
|
|
@ -1,30 +1,40 @@
|
||||||
import 'dart:io';
|
|
||||||
import 'package:angel_diagnostics/angel_diagnostics.dart';
|
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_test/angel_test.dart';
|
import 'package:angel_test/angel_test.dart';
|
||||||
import 'package:angel_validate/server.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';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
final Validator echoSchema = new Validator({'message*': isString});
|
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() {
|
main() {
|
||||||
Angel app;
|
Angel app;
|
||||||
|
AngelHttp http;
|
||||||
TestClient client;
|
TestClient client;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = new Angel();
|
app = new Angel();
|
||||||
|
http = new AngelHttp(app, useZone: false);
|
||||||
|
|
||||||
app.chain(validate(echoSchema)).post('/echo',
|
app.chain(validate(echoSchema)).post('/echo',
|
||||||
(RequestContext req, res) async {
|
(RequestContext req, res) async {
|
||||||
res.write('Hello, ${req.body['message']}!');
|
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);
|
client = await connectTo(app);
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
await client.close();
|
await client.close();
|
||||||
|
await http.close();
|
||||||
app = null;
|
app = null;
|
||||||
client = null;
|
client = null;
|
||||||
});
|
});
|
||||||
|
@ -32,17 +42,23 @@ main() {
|
||||||
group('echo', () {
|
group('echo', () {
|
||||||
test('validate', () async {
|
test('validate', () async {
|
||||||
var response = await client.post('/echo',
|
var response = await client.post('/echo',
|
||||||
body: {'message': 'world'}, headers: {HttpHeaders.ACCEPT: '*/*'});
|
body: {'message': 'world'}, headers: {'accept': '*/*'});
|
||||||
print('Response: ${response.body}');
|
print('Response: ${response.body}');
|
||||||
expect(response, hasStatus(HttpStatus.OK));
|
expect(response, hasStatus(200));
|
||||||
expect(response.body, equals('Hello, world!'));
|
expect(response.body, equals('Hello, world!'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('enforce', () async {
|
test('enforce', () async {
|
||||||
var response = await client.post('/echo',
|
var rq = new MockHttpRequest('POST', new Uri(path: '/echo'))
|
||||||
body: {'foo': 'bar'}, headers: {HttpHeaders.ACCEPT: '*/*'});
|
..headers.add('accept', '*/*')
|
||||||
print('Response: ${response.body}');
|
..headers.add('content-type', 'application/json')
|
||||||
expect(response, hasStatus(HttpStatus.BAD_REQUEST));
|
..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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
|
|
Loading…
Reference in a new issue