Merge pull request #110 from dukefirehawk/bug-fix/validate
Bug fix/validate
This commit is contained in:
commit
eb6d4e6d10
14 changed files with 120 additions and 62 deletions
|
@ -1,5 +1,10 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 8.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 3.0
|
||||||
|
* Updated `oauth1` to `belatuk_oauth1`
|
||||||
|
|
||||||
## 7.0.0
|
## 7.0.0
|
||||||
|
|
||||||
* Require Dart >= 2.17
|
* Require Dart >= 2.17
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2021, dukefirehawk.com
|
Copyright (c) 2023, dukefirehawk.com
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
|
@ -6,29 +6,28 @@ repository: https://github.com/dukefirehawk/angel/tree/master/packages/auth_twit
|
||||||
publish_to: none
|
publish_to: none
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
published_to: none
|
|
||||||
dependencies:
|
dependencies:
|
||||||
angel3_auth: ^8.0.0
|
angel3_auth: ^8.0.0
|
||||||
angel3_framework: ^8.0.0
|
angel3_framework: ^8.0.0
|
||||||
http: ^1.0.0
|
http: ^1.0.0
|
||||||
path: ^1.0.0
|
path: ^1.0.0
|
||||||
oauth1: ^2.0.0
|
belatuk_oauth1: ^3.0.0
|
||||||
dart_twitter_api: ^0.5.6+1
|
dart_twitter_api: ^0.5.6+1
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
logging: ^1.2.0
|
logging: ^1.2.0
|
||||||
lints: ^2.1.0
|
lints: ^2.1.0
|
||||||
dependency_overrides:
|
# dependency_overrides:
|
||||||
angel3_container:
|
# angel3_container:
|
||||||
path: ../container/angel_container
|
# path: ../container/angel_container
|
||||||
angel3_framework:
|
# angel3_framework:
|
||||||
path: ../framework
|
# path: ../framework
|
||||||
angel3_http_exception:
|
# angel3_http_exception:
|
||||||
path: ../http_exception
|
# path: ../http_exception
|
||||||
angel3_model:
|
# angel3_model:
|
||||||
path: ../model
|
# path: ../model
|
||||||
angel3_route:
|
# angel3_route:
|
||||||
path: ../route
|
# path: ../route
|
||||||
angel3_mock_request:
|
# angel3_mock_request:
|
||||||
path: ../mock_request
|
# path: ../mock_request
|
||||||
angel3_auth:
|
# angel3_auth:
|
||||||
path: ../auth
|
# path: ../auth
|
|
@ -16,12 +16,12 @@ dependencies:
|
||||||
logging: ^1.2.0
|
logging: ^1.2.0
|
||||||
charcode: ^1.3.0
|
charcode: ^1.3.0
|
||||||
http: ^1.0.0
|
http: ^1.0.0
|
||||||
# dev_dependencies:
|
dev_dependencies:
|
||||||
# angel3_test: ^8.0.0
|
angel3_test: ^8.0.0
|
||||||
# belatuk_pretty_logging: ^6.0.0
|
belatuk_pretty_logging: ^6.0.0
|
||||||
# shelf_static: ^1.1.0
|
shelf_static: ^1.1.0
|
||||||
# test: ^1.24.0
|
test: ^1.24.0
|
||||||
# lints: ^2.1.0
|
lints: ^2.1.0
|
||||||
# dependency_overrides:
|
# dependency_overrides:
|
||||||
# angel3_test:
|
# angel3_test:
|
||||||
# path: ../test
|
# path: ../test
|
||||||
|
|
|
@ -16,7 +16,7 @@ void main() {
|
||||||
late http.Client client;
|
late http.Client client;
|
||||||
late HttpServer server;
|
late HttpServer server;
|
||||||
|
|
||||||
Uri _path(String p) {
|
Uri path(String p) {
|
||||||
return Uri(
|
return Uri(
|
||||||
scheme: 'http',
|
scheme: 'http',
|
||||||
host: server.address.address,
|
host: server.address.address,
|
||||||
|
@ -65,25 +65,25 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('expose angel side', () async {
|
test('expose angel side', () async {
|
||||||
var response = await client.get(_path('/angel'));
|
var response = await client.get(path('/angel'));
|
||||||
expect(json.decode(response.body), equals('Angel'));
|
expect(json.decode(response.body), equals('Angel'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('expose shelf side', () async {
|
test('expose shelf side', () async {
|
||||||
var response = await client.get(_path('/foo'));
|
var response = await client.get(path('/foo'));
|
||||||
expect(response, hasStatus(200));
|
expect(response, hasStatus(200));
|
||||||
expect(response.body, equals('Request for "foo"'));
|
expect(response.body, equals('Request for "foo"'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shelf can return arbitrary values', () async {
|
test('shelf can return arbitrary values', () async {
|
||||||
var response = await client.get(_path('/two'));
|
var response = await client.get(path('/two'));
|
||||||
expect(response, isJson(2));
|
expect(response, isJson(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shelf can hijack', () async {
|
test('shelf can hijack', () async {
|
||||||
try {
|
try {
|
||||||
var client = HttpClient();
|
var client = HttpClient();
|
||||||
var rq = await client.openUrl('GET', _path('/hijack'));
|
var rq = await client.openUrl('GET', path('/hijack'));
|
||||||
var rs = await rq.close();
|
var rs = await rq.close();
|
||||||
var body = await rs.cast<List<int>>().transform(utf8.decoder).join();
|
var body = await rs.cast<List<int>>().transform(utf8.decoder).join();
|
||||||
print('Response: $body');
|
print('Response: $body');
|
||||||
|
@ -96,17 +96,17 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shelf can set status code', () async {
|
test('shelf can set status code', () async {
|
||||||
var response = await client.get(_path('/status'));
|
var response = await client.get(path('/status'));
|
||||||
expect(response, allOf(hasStatus(304), hasHeader('foo', 'bar')));
|
expect(response, allOf(hasStatus(304), hasHeader('foo', 'bar')));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shelf can throw error', () async {
|
test('shelf can throw error', () async {
|
||||||
var response = await client.get(_path('/error'));
|
var response = await client.get(path('/error'));
|
||||||
expect(response, hasStatus(404));
|
expect(response, hasStatus(404));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throw on null', () async {
|
test('throw on null', () async {
|
||||||
var response = await client.get(_path('/throw'));
|
var response = await client.get(path('/throw'));
|
||||||
expect(response, hasStatus(500));
|
expect(response, hasStatus(500));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Angel3 Testing Library
|
# Angel3 Test
|
||||||
|
|
||||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_test?include_prereleases)
|
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_test?include_prereleases)
|
||||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/test/LICENSE)
|
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/test/LICENSE)
|
||||||
|
|
||||||
Testing utility library for the Angel3 framework.
|
Testing utility library for Angel3 framework.
|
||||||
|
|
||||||
## TestClient
|
## TestClient
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ void test('error', () async {
|
||||||
```dart
|
```dart
|
||||||
test('validate response', () async {
|
test('validate response', () async {
|
||||||
var res = await client.get('/bar');
|
var res = await client.get('/bar');
|
||||||
expect(res, hasValidBody(new Validator({
|
expect(res, hasValidBody(Validator({
|
||||||
'foo': isBoolean,
|
'foo': isBoolean,
|
||||||
'bar': [isString, equals('baz')],
|
'bar': [isString, equals('baz')],
|
||||||
'age*': [],
|
'age*': [],
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
# Angel3 Request Validator
|
# Angel3 Validate
|
||||||
|
|
||||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_validate?include_prereleases)
|
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_validate?include_prereleases)
|
||||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/validate/LICENSE)
|
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/validate/LICENSE)
|
||||||
|
|
||||||
Validation library based on the `matcher` library, with Angel3 support. Why re-invent the wheel, when you can use the same validators you already use for tests?
|
This validator library is based on the `matcher` library and comes with build in support for Angel3 framework. It can be run on both server and client side. Thus, the same validation rules apply to forms on both backend and frontend code.
|
||||||
|
|
||||||
This library runs both on the server, and on the client. Thus, you can use the same validation rules for forms on the server, and on the frontend.
|
|
||||||
|
|
||||||
For convenience's sake, this library also exports `matcher`.
|
For convenience's sake, this library also exports `matcher`.
|
||||||
|
|
||||||
- [Angel3 Request Validator](#angel3-request-validator)
|
- [Angel3 Validate](#angel3-validate)
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
- [Creating a Validator](#creating-a-validator)
|
- [Creating a Validator](#creating-a-validator)
|
||||||
- [Validating data](#validating-data)
|
- [Validating data](#validating-data)
|
||||||
|
@ -25,7 +23,7 @@ For convenience's sake, this library also exports `matcher`.
|
||||||
- [Extending Validators](#extending-validators)
|
- [Extending Validators](#extending-validators)
|
||||||
- [Bundled Matchers](#bundled-matchers)
|
- [Bundled Matchers](#bundled-matchers)
|
||||||
- [Nested Validators](#nested-validators)
|
- [Nested Validators](#nested-validators)
|
||||||
- [Use with Angel](#use-with-angel)
|
- [Use with Angel3](#use-with-angel3)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
@ -82,9 +80,7 @@ main() {
|
||||||
|
|
||||||
### Required Fields
|
### Required Fields
|
||||||
|
|
||||||
Fields are optional by default.
|
Fields are optional by default. Suffix a field name with a `'*'` to mark it as required, and to throw an error if it is not present.
|
||||||
|
|
||||||
Suffix a field name with a `'*'` to mark it as required, and to throw an error if it is not present.
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
main() {
|
main() {
|
||||||
|
@ -118,9 +114,7 @@ Default values can also be parameterless, *synchronous* functions that return a
|
||||||
|
|
||||||
### Custom Validator Functions
|
### Custom Validator Functions
|
||||||
|
|
||||||
Creating a whole `Matcher` class is sometimes cumbersome, but if you pass a function to the constructor, it will be wrapped in a `Matcher` instance.
|
Creating a whole `Matcher` class is sometimes cumbersome, but if you pass a function to the constructor, it will be wrapped in a `Matcher` instance. (It simply returns the value of calling [`predicate`](https://pub.dev/documentation/matcher/latest/matcher/predicate.html).)
|
||||||
|
|
||||||
(It simply returns the value of calling [`predicate`](https://pub.dev/documentation/matcher/latest/matcher/predicate.html).)
|
|
||||||
|
|
||||||
The function must *synchronously* return a `bool`.
|
The function must *synchronously* return a `bool`.
|
||||||
|
|
||||||
|
@ -151,8 +145,7 @@ The string `{{value}}` will be replaced inside your error message automatically.
|
||||||
|
|
||||||
### autoParse
|
### autoParse
|
||||||
|
|
||||||
Oftentimes, fields that we want to validate as numbers are passed as strings.
|
Oftentimes, fields that we want to validate as numbers are passed as strings. Calling `autoParse` will correct this before validation.
|
||||||
Calling `autoParse` will correct this before validation.
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
main() {
|
main() {
|
||||||
|
@ -263,7 +256,7 @@ main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use with Angel
|
### Use with Angel3
|
||||||
|
|
||||||
`server.dart` exposes seven helper middleware:
|
`server.dart` exposes seven helper middleware:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: angel3_validate
|
name: angel3_validate
|
||||||
description: Cross-platform request body validation library based on `matcher`.
|
description: Cross-platform HTTP request body validator library based on `matcher`.
|
||||||
version: 8.0.0
|
version: 8.0.0
|
||||||
homepage: https://angel3-framework.web.app/
|
homepage: https://angel3-framework.web.app/
|
||||||
repository: https://github.com/dukefirehawk/angel/tree/master/packages/validate
|
repository: https://github.com/dukefirehawk/angel/tree/master/packages/validate
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
void main() {}
|
|
|
@ -31,8 +31,7 @@ void main() {
|
||||||
expect(() {
|
expect(() {
|
||||||
todoSchema
|
todoSchema
|
||||||
.enforce({'id': 'fool', 'text': 'Hello, world!', 'completed': 4});
|
.enforce({'id': 'fool', 'text': 'Hello, world!', 'completed': 4});
|
||||||
// ignore: deprecated_member_use
|
}, throwsA(isA<ValidationException>()));
|
||||||
}, throwsA(isInstanceOf<ValidationException>()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('filter', () {
|
test('filter', () {
|
66
packages/validate/test/complex_data_test.dart
Normal file
66
packages/validate/test/complex_data_test.dart
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import 'package:angel3_validate/angel3_validate.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final Validator orderItemSchema = Validator({
|
||||||
|
'id': [isInt, isPositive],
|
||||||
|
'item_no': isString,
|
||||||
|
'item_name': isString,
|
||||||
|
'quantity': isInt,
|
||||||
|
'description?': isString
|
||||||
|
});
|
||||||
|
|
||||||
|
final Validator orderSchema = Validator({
|
||||||
|
'id': [isInt, isPositive],
|
||||||
|
'order_no': isString,
|
||||||
|
'order_items*': [isList, everyElement(orderItemSchema)]
|
||||||
|
}, defaultValues: {
|
||||||
|
'order_items': []
|
||||||
|
});
|
||||||
|
|
||||||
|
group('json data', () {
|
||||||
|
test('validate with child element', () {
|
||||||
|
var orderItem = {
|
||||||
|
'id': 1,
|
||||||
|
'item_no': 'a1',
|
||||||
|
'item_name': 'Apple',
|
||||||
|
'quantity': 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var formData = {
|
||||||
|
'id': 1,
|
||||||
|
'order_no': '2',
|
||||||
|
'order_items': [orderItem]
|
||||||
|
};
|
||||||
|
var result = orderSchema.check(formData);
|
||||||
|
|
||||||
|
expect(result.errors.isEmpty, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate empty child', () {
|
||||||
|
var formData = {'id': 1, 'order_no': '2'};
|
||||||
|
var result = orderSchema.check(formData);
|
||||||
|
|
||||||
|
expect(result.errors.isEmpty, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate invalid child field', () {
|
||||||
|
var orderItem = {
|
||||||
|
'id': 1,
|
||||||
|
'item_no': 'a1',
|
||||||
|
'item_name': 'Apple',
|
||||||
|
'quantity': 1,
|
||||||
|
'description': 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var formData = {
|
||||||
|
'id': 1,
|
||||||
|
'order_no': '2',
|
||||||
|
'order_items': [orderItem]
|
||||||
|
};
|
||||||
|
var result = orderSchema.check(formData);
|
||||||
|
|
||||||
|
expect(result.errors.isEmpty, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
void main() {}
|
|
|
@ -18,29 +18,27 @@ void printRecord(LogRecord rec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Angel? app;
|
late Angel app;
|
||||||
late AngelHttp http;
|
late AngelHttp http;
|
||||||
//TestClient client;
|
//TestClient client;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = Angel();
|
app = Angel();
|
||||||
http = AngelHttp(app!, useZone: false);
|
http = AngelHttp(app, useZone: false);
|
||||||
|
|
||||||
app!.chain([validate(echoSchema)]).post('/echo',
|
app.chain([validate(echoSchema)]).post('/echo',
|
||||||
(RequestContext req, res) async {
|
(RequestContext req, res) async {
|
||||||
await req.parseBody();
|
await req.parseBody();
|
||||||
res.write('Hello, ${req.bodyAsMap['message']}!');
|
res.write('Hello, ${req.bodyAsMap['message']}!');
|
||||||
});
|
});
|
||||||
|
|
||||||
app!.logger = Logger('angel')..onRecord.listen(printRecord);
|
app.logger = Logger('angel3')..onRecord.listen(printRecord);
|
||||||
//client = await connectTo(app);
|
//client = await connectTo(app);
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
//await client.close();
|
//await client.close();
|
||||||
await http.close();
|
await http.close();
|
||||||
app = null;
|
|
||||||
//client = null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
group('echo', () {
|
group('echo', () {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>angel_validate</title>
|
<title>angel3_validate</title>
|
||||||
<meta name="viewport"
|
<meta name="viewport"
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
<style>
|
<style>
|
||||||
|
|
Loading…
Reference in a new issue