Skip to content

Commit 9b0ee3e

Browse files
committed
feat: support example method to enhance openapi reporter
1 parent b25b98c commit 9b0ee3e

File tree

11 files changed

+172
-42
lines changed

11 files changed

+172
-42
lines changed

benchmark/array_object.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ void main() {
2424
iterationCount++;
2525
}
2626

27-
print('Array Object : Processed $iterationCount iterations in ${duration.inSeconds} seconds');
27+
print(
28+
'Array Object : Processed $iterationCount iterations in ${duration.inSeconds} seconds');
2829
}

lib/src/contracts/schema.dart

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import 'package:vine/src/contracts/vine.dart';
22
import 'package:vine/src/introspection.dart';
33
import 'package:vine/src/schema/object/object_schema.dart';
44

5-
abstract interface class VineSchema<T extends ErrorReporter> implements SchemaIntrospection {
5+
abstract interface class VineSchema<T extends ErrorReporter>
6+
implements SchemaIntrospection {
67
/// Validate the field [field] the field to validate
78
void parse(VineValidationContext ctx, FieldContext field);
89

@@ -63,7 +64,8 @@ abstract interface class BasicSchema<T extends VineSchema> {
6364
T optional();
6465
}
6566

66-
abstract interface class VineString implements VineSchema, BasicSchema<VineString> {
67+
abstract interface class VineString
68+
implements VineSchema, BasicSchema<VineString> {
6769
/// Check if the string has a minimum length [value] the minimum length [message] the error message to display
6870
/// ```dart
6971
/// vine.string().minLength(5);
@@ -203,7 +205,8 @@ abstract interface class VineString implements VineSchema, BasicSchema<VineStrin
203205
/// vine.string().confirmed(
204206
/// message: 'The value must be confirmed');
205207
/// ```
206-
VineString confirmed({String? property, bool include = false, String? message});
208+
VineString confirmed(
209+
{String? property, bool include = false, String? message});
207210

208211
/// Remove leading and trailing whitespace from the string
209212
/// ```dart
@@ -321,9 +324,16 @@ abstract interface class VineString implements VineSchema, BasicSchema<VineStrin
321324
/// message: 'The value must contain only alphabetic characters');
322325
/// ```
323326
VineString regex(RegExp expression, {String? message});
327+
328+
/// Set the example value for the string [value] the example value
329+
/// ```dart
330+
/// vine.string().example('foo');
331+
/// ```
332+
VineString example(String value);
324333
}
325334

326-
abstract interface class VineNumber implements VineSchema, BasicSchema<VineNumber> {
335+
abstract interface class VineNumber
336+
implements VineSchema, BasicSchema<VineNumber> {
327337
/// Check if the number is in a range of values [values] the range of values [message] the error message to display
328338
/// ```dart
329339
/// vine.number().range([1, 10]);
@@ -392,15 +402,42 @@ abstract interface class VineNumber implements VineSchema, BasicSchema<VineNumbe
392402
/// You can specify a custom error message
393403
/// ```dart
394404
VineNumber integer({String? message});
405+
406+
/// Set the example value for the number [value] the example value
407+
/// ```dart
408+
/// vine.number().example(10);
409+
/// ```
410+
VineNumber example(num value);
395411
}
396412

397-
abstract interface class VineBoolean implements VineSchema, BasicSchema<VineBoolean> {}
413+
abstract interface class VineBoolean
414+
implements VineSchema, BasicSchema<VineBoolean> {
415+
/// Set the example value for the boolean [value] the example value
416+
/// ```dart
417+
/// vine.boolean().example(true);
418+
/// ```
419+
VineBoolean example(bool value);
420+
}
398421

399-
abstract interface class VineAny implements VineSchema, BasicSchema<VineAny> {}
422+
abstract interface class VineAny implements VineSchema, BasicSchema<VineAny> {
423+
/// Set the example value for the any [value] the example value
424+
/// ```dart
425+
/// vine.any().example('foo');
426+
/// ```
427+
VineAny example(dynamic value);
428+
}
400429

401-
abstract interface class VineEnum implements VineSchema, BasicSchema<VineEnum> {}
430+
abstract interface class VineEnum<T extends VineEnumerable>
431+
implements VineSchema, BasicSchema<VineEnum<T>> {
432+
/// Set the example value for the enum [value] the example value
433+
/// ```dart
434+
/// vine.enum().example('foo');
435+
/// ```
436+
VineEnum<T> example(T value);
437+
}
402438

403-
abstract interface class VineObject implements VineSchema, BasicSchema<VineObject> {
439+
abstract interface class VineObject
440+
implements VineSchema, BasicSchema<VineObject> {
404441
Map<String, VineSchema> get properties;
405442

406443
VineObject merge(VineObjectSchema schema);
@@ -415,7 +452,8 @@ abstract interface class VineGroup implements VineSchema {
415452
/// 'email': vine.string().required().email(),
416453
/// });
417454
/// });
418-
VineGroup when(bool Function(Map<String, dynamic> data) fn, Map<String, VineSchema> object);
455+
VineGroup when(bool Function(Map<String, dynamic> data) fn,
456+
Map<String, VineSchema> object);
419457

420458
/// Apply the schema if the condition is not met [fn] the schema to apply
421459
/// ```dart
@@ -432,7 +470,8 @@ abstract interface class VineGroup implements VineSchema {
432470
VineGroup otherwise(Function(VineValidationContext, FieldContext) fn);
433471
}
434472

435-
abstract interface class VineArray implements VineSchema, BasicSchema<VineArray> {
473+
abstract interface class VineArray
474+
implements VineSchema, BasicSchema<VineArray> {
436475
/// Check if the array has a minimum length [value] the minimum length [message] the error message to display
437476
/// ```dart
438477
/// vine.array().minLength(5);
@@ -474,7 +513,10 @@ abstract interface class VineArray implements VineSchema, BasicSchema<VineArray>
474513
VineArray unique({String? message});
475514
}
476515

477-
abstract interface class VineUnion implements VineSchema, BasicSchema<VineUnion> {}
516+
abstract interface class VineUnion
517+
implements VineSchema, BasicSchema<VineUnion> {
518+
VineUnion example(dynamic value);
519+
}
478520

479521
abstract interface class VineDate implements VineSchema, BasicSchema<VineDate> {
480522
/// Check if the date is before [value] the date to compare [message] the error message to display
@@ -539,4 +581,10 @@ abstract interface class VineDate implements VineSchema, BasicSchema<VineDate> {
539581
/// message: 'The date must be between the start and end date');
540582
/// ```
541583
VineDate betweenFields(String start, String end, {String? message});
584+
585+
/// Set the example value for the date [value] the example value
586+
/// ```dart
587+
/// vine.date().example(DateTime.now());
588+
/// ```
589+
VineDate example(String value);
542590
}

lib/src/schema/any_schema.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import 'package:vine/src/rule_parser.dart';
66
import 'package:vine/src/rules/basic_rule.dart';
77

88
final class VineAnySchema extends RuleParser implements VineAny {
9+
dynamic _example;
10+
911
VineAnySchema(super._rules);
1012

1113
@override
@@ -50,6 +52,12 @@ final class VineAnySchema extends RuleParser implements VineAny {
5052
return this;
5153
}
5254

55+
@override
56+
VineAny example(dynamic value) {
57+
_example = value;
58+
return this;
59+
}
60+
5361
@override
5462
VineAny clone() {
5563
return VineAnySchema(Queue.of(rules));
@@ -59,6 +67,7 @@ final class VineAnySchema extends RuleParser implements VineAny {
5967
Map<String, dynamic> introspect({String? name}) {
6068
return {
6169
'required': !isOptional,
70+
if (_example != null) 'example': _example,
6271
};
6372
}
6473
}

lib/src/schema/boolean_schema.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import 'package:vine/src/rule_parser.dart';
66
import 'package:vine/src/rules/basic_rule.dart';
77

88
final class VineBooleanSchema extends RuleParser implements VineBoolean {
9+
dynamic _example;
10+
911
VineBooleanSchema(super._rules);
1012

1113
@override
@@ -50,6 +52,12 @@ final class VineBooleanSchema extends RuleParser implements VineBoolean {
5052
return this;
5153
}
5254

55+
@override
56+
VineBoolean example(bool value) {
57+
_example = value;
58+
return this;
59+
}
60+
5361
@override
5462
VineBoolean clone() {
5563
return VineBooleanSchema(Queue.of(rules));
@@ -60,7 +68,7 @@ final class VineBooleanSchema extends RuleParser implements VineBoolean {
6068
return {
6169
'type': 'boolean',
6270
'required': !isOptional,
63-
'example': true,
71+
'example': _example ?? true,
6472
};
6573
}
6674
}

lib/src/schema/date_schema.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import 'package:vine/src/rules/basic_rule.dart';
77
import 'package:vine/src/rules/date_rule.dart';
88

99
final class VineDateSchema extends RuleParser implements VineDate {
10+
dynamic _example;
11+
1012
VineDateSchema(super._rules);
1113

1214
@override
@@ -87,6 +89,12 @@ final class VineDateSchema extends RuleParser implements VineDate {
8789
return this;
8890
}
8991

92+
@override
93+
VineDate example(String value) {
94+
_example = value;
95+
return this;
96+
}
97+
9098
@override
9199
VineDate clone() {
92100
return VineDateSchema(Queue.of(rules));
@@ -98,7 +106,7 @@ final class VineDateSchema extends RuleParser implements VineDate {
98106
'type': 'string',
99107
'format': 'date-time',
100108
'required': !isOptional,
101-
'example': DateTime.now().toIso8601String(),
109+
if (_example != null) 'example': _example,
102110
};
103111
}
104112
}

lib/src/schema/enum_schema.dart

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,72 @@ import 'dart:collection';
33
import 'package:vine/vine.dart';
44

55
final class VineEnumSchema<T extends VineEnumerable> extends RuleParser
6-
implements VineEnum {
6+
implements VineEnum<T> {
77
final List<T> _source;
8+
T? _example;
9+
810
VineEnumSchema(super._rules, this._source);
911

1012
@override
11-
VineEnum requiredIfExist(List<String> values) {
13+
VineEnum<T> requiredIfExist(List<String> values) {
1214
super.addRule(VineRequiredIfExistRule(values), positioned: true);
1315
return this;
1416
}
1517

1618
@override
17-
VineEnum requiredIfAnyExist(List<String> values) {
19+
VineEnum<T> requiredIfAnyExist(List<String> values) {
1820
super.addRule(VineRequiredIfAnyExistRule(values), positioned: true);
1921
return this;
2022
}
2123

2224
@override
23-
VineEnum requiredIfMissing(List<String> values) {
25+
VineEnum<T> requiredIfMissing(List<String> values) {
2426
super.addRule(VineRequiredIfMissingRule(values), positioned: true);
2527
return this;
2628
}
2729

2830
@override
29-
VineEnum requiredIfAnyMissing(List<String> values) {
31+
VineEnum<T> requiredIfAnyMissing(List<String> values) {
3032
super.addRule(VineRequiredIfAnyMissingRule(values), positioned: true);
3133
return this;
3234
}
3335

3436
@override
35-
VineEnum transform(Function(VineValidationContext, FieldContext) fn) {
37+
VineEnum<T> transform(Function(VineValidationContext, FieldContext) fn) {
3638
super.addRule(VineTransformRule(fn));
3739
return this;
3840
}
3941

4042
@override
41-
VineEnum nullable() {
43+
VineEnum<T> nullable() {
4244
super.isNullable = true;
4345
return this;
4446
}
4547

4648
@override
47-
VineEnum optional() {
49+
VineEnum<T> optional() {
4850
super.isOptional = true;
4951
return this;
5052
}
5153

5254
@override
53-
VineEnum clone() {
55+
VineEnum<T> example(T value) {
56+
_example = value;
57+
return this;
58+
}
59+
60+
@override
61+
VineEnum<T> clone() {
5462
return VineEnumSchema(Queue.of(rules), _source.toList());
5563
}
5664

5765
@override
5866
Map<String, dynamic> introspect({String? name}) {
5967
return {
60-
'type': 'string', // Adapt selon le type des valeurs
68+
'type': 'string',
6169
'enum': _source.map((e) => e.value).toList(),
6270
'required': !isOptional,
63-
'example': _source.firstOrNull?.value,
71+
'example': _example?.value ?? _source.first.value,
6472
};
6573
}
6674
}

lib/src/schema/number_schema.dart

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import 'package:vine/src/rules/basic_rule.dart';
77
import 'package:vine/src/rules/number_rule.dart';
88

99
final class VineNumberSchema extends RuleParser implements VineNumber {
10+
dynamic _example;
11+
1012
VineNumberSchema(super._rules);
1113

1214
@override
@@ -93,6 +95,12 @@ final class VineNumberSchema extends RuleParser implements VineNumber {
9395
return this;
9496
}
9597

98+
@override
99+
VineNumber example(num value) {
100+
_example = value;
101+
return this;
102+
}
103+
96104
@override
97105
VineNumber clone() {
98106
return VineNumberSchema(Queue.of(rules));
@@ -134,10 +142,15 @@ final class VineNumberSchema extends RuleParser implements VineNumber {
134142
}
135143

136144
// Détermination du type
137-
final type = isInteger ? 'integer' : isDouble ? 'number' : 'number';
145+
final type = isInteger
146+
? 'integer'
147+
: isDouble
148+
? 'number'
149+
: 'number';
138150

139151
// Validation de l'exemple
140-
if (validations.containsKey('minimum') || validations.containsKey('maximum')) {
152+
if (validations.containsKey('minimum') ||
153+
validations.containsKey('maximum')) {
141154
example = example.clamp(
142155
validations['minimum'] ?? '-Infinity',
143156
validations['maximum'] ?? 'Infinity',
@@ -150,7 +163,7 @@ final class VineNumberSchema extends RuleParser implements VineNumber {
150163
'type': type,
151164
'required': !isOptional,
152165
if (enums != null) 'enum': enums,
153-
'example': example,
166+
'example': _example ?? example,
154167
...validations,
155168
}..removeWhere((_, v) => v == null);
156169
}

lib/src/schema/object/object_schema.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ final class VineObjectSchema extends RuleParser implements VineObject {
8181
schema.remove('required');
8282
}
8383

84-
example[entry.key] = schema['example'] ?? (schema['examples'] as List).firstOrNull;
84+
example[entry.key] = schema['example'] ??
85+
(schema['examples'] is List
86+
? (schema['examples'] as List).firstOrNull
87+
: null);
8588
}
8689

8790
return {

0 commit comments

Comments
 (0)