Skip to content

Commit

Permalink
fix: remove unsupported client-side validators @PastOrPresent and @Fu…
Browse files Browse the repository at this point in the history
…tureOrPresent (#8684)

It's not trivial to ensure the same granularity of _present_ as on the server-side: year / month / day / minute.
Until there is a sensible client-side implementation that works close enough to the server-side, it's better not to have any client-side validation for these constraints than to have a broken implementation.

NOTE: JSR-380 server-side validators interpret 'present' depending on the Java property type (could be the whole current year for `java.time.Year` (see https://beanvalidation.org/2.0-jsr380/spec/#builtinconstraints-pastorpresent)
  • Loading branch information
Viktor Lukashov committed Jul 6, 2020
1 parent 7337310 commit f987bb9
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -245,24 +245,37 @@ export class Past extends AbstractValidator<any> {
}
validate = (value: any) => isBefore(value);
}
export class PastOrPresent extends AbstractValidator<any> {
constructor(attrs?: ValidatorAttributes) {
super({ message: 'must be a date in the past or in the present', ...attrs });
}
validate = () => { throw new Error('Form Validator for PastOrPresent not implemented yet') };
}
/*
@PastOrPresent has no client-side implementation yet.
It would consider any input valid and let the server-side to do validation.
(It's not trivial to ensure the same granularity of _present_ as on the server-side:
year / month / day / minute).
*/
// export class PastOrPresent extends AbstractValidator<any> {
// constructor(attrs?: ValidatorAttributes) {
// super({ message: 'must be a date in the past or in the present', ...attrs });
// }
// validate = () => true;
// }
export class Future extends AbstractValidator<any> {
constructor(attrs?: ValidatorAttributes) {
super({ message: 'must be a future date', ...attrs });
}
validate = (value: any) => isAfter(value);
}
export class FutureOrPresent extends AbstractValidator<any> {
constructor(attrs?: ValidatorAttributes) {
super({ message: 'must be a date in the present or in the future', ...attrs });
}
validate = () => { throw new Error('Form Validator for FutureOrPresent not implemented yet') };
}

/*
@FutureOrPresent has no client-side implementation yet.
It would consider any input valid and let the server-side to do validation.
(It's not trivial to ensure the same granularity of _present_ as on the server-side:
year / month / day / minute).
*/
// export class FutureOrPresent extends AbstractValidator<any> {
// constructor(attrs?: ValidatorAttributes) {
// super({ message: 'must be a date in the present or in the future', ...attrs });
// }
// validate = () => true;
// }

function _regexp(attrs: PatternAttributes | string | RegExp) {
return typeof attrs === 'string' ? new RegExp(attrs)
Expand Down
22 changes: 16 additions & 6 deletions flow-client/src/test/frontend/form/ValidatorsTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,23 +223,33 @@ suite("form/Validators", () => {
test("Past", () => {
const validator = new Past();
assert.isNotTrue(validator.impliesRequired);
assert.isTrue(validator.validate("2019-12-31"));
assert.isFalse(validator.validate(String(new Date())));
assert.isFalse(validator.validate("3000-01-01"));
assert.isTrue(validator.validate("2019-12-31"), 'past');
assert.isFalse(validator.validate(String(new Date())), 'present');
assert.isFalse(validator.validate("3000-01-01"), 'future');
});

// test("PastOrPresent", () => {
// const validator = new PastOrPresent();
// assert.isNotTrue(validator.impliesRequired);
// assert.isTrue(validator.validate("2019-12-31"), 'past');
// assert.isTrue(validator.validate(String(new Date())), 'present');
// assert.isFalse(validator.validate("3000-01-01"), 'future');
// });

test("Future", () => {
const validator = new Future();
assert.isNotTrue(validator.impliesRequired);
assert.isFalse(validator.validate("2019-12-31"));
assert.isFalse(validator.validate(String(new Date())));
assert.isTrue(validator.validate("3000-01-01"));
assert.isFalse(validator.validate("2019-12-31"), 'past');
assert.isFalse(validator.validate(String(new Date())), 'present');
assert.isTrue(validator.validate("3000-01-01"), 'future');
});

// test("FutureOrPresent", () => {
// const validator = new FutureOrPresent();
// assert.isNotTrue(validator.impliesRequired);
// assert.isFalse(validator.validate("2019-12-31"), 'past');
// assert.isTrue(validator.validate(String(new Date())), 'present');
// assert.isTrue(validator.validate("3000-01-01"), 'future');
// });

test("Pattern", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ private void addFieldAnnotationsToSchema(FieldDeclaration field,
str += str.contains("(") ? "" : "()";

if (str.matches(
"(Email|Null|NotNull|NotEmpty|NotBlank|AssertTrue|AssertFalse|Negative|NegativeOrZero|Positive|PositiveOrZero|Size|Past|PastOrPresent|Future|FutureOrPresent|Digits|Min|Max|Pattern|DecimalMin|DecimalMax)\\(.+")) {
"(Email|Null|NotNull|NotEmpty|NotBlank|AssertTrue|AssertFalse|Negative|NegativeOrZero|Positive|PositiveOrZero|Size|Past|Future|Digits|Min|Max|Pattern|DecimalMin|DecimalMax)\\(.+")) {
annotations.add(str);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {{{getClassNameFromImports classname ../../imports}}} from './{{{getClass
import {ObjectModel,StringModel,NumberModel,ArrayModel,BooleanModel,Required,ModelType} from '@vaadin/form';

// @ts-ignore
import {Email,Null,NotNull,NotEmpty,NotBlank,AssertTrue,AssertFalse,Negative,NegativeOrZero,Positive,PositiveOrZero,Size,Past,PastOrPresent,Future,FutureOrPresent,Digits,Min,Max,Pattern,DecimalMin,DecimalMax} from '@vaadin/form';
import {Email,Null,NotNull,NotEmpty,NotBlank,AssertTrue,AssertFalse,Negative,NegativeOrZero,Positive,PositiveOrZero,Size,Past,Future,Digits,Min,Max,Pattern,DecimalMin,DecimalMax} from '@vaadin/form';

{{#models}}
{{#model}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.Future;
import javax.validation.constraints.FutureOrPresent;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Negative;
Expand All @@ -33,7 +32,6 @@
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Past;
import javax.validation.constraints.PastOrPresent;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Positive;
import javax.validation.constraints.PositiveOrZero;
Expand Down Expand Up @@ -94,9 +92,7 @@ public static class MyEntity extends MyEntityId {
@Size(min = 1) String size1;
@Digits(integer=5, fraction = 2) String digits;
@Past LocalDate past;
@PastOrPresent LocalTime pastOrPresent;
@Future LocalDate future;
@FutureOrPresent LocalTime futureOrPresent;
@Pattern(regexp = "\\d+\\..+") String pattern;
List<MyEntity> children;
String[] stringArray;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import MyEntity from './MyEntity';
import {ObjectModel,StringModel,NumberModel,ArrayModel,BooleanModel,Required,ModelType} from '@vaadin/form';

// @ts-ignore
import {Email,Null,NotNull,NotEmpty,NotBlank,AssertTrue,AssertFalse,Negative,NegativeOrZero,Positive,PositiveOrZero,Size,Past,PastOrPresent,Future,FutureOrPresent,Digits,Min,Max,Pattern,DecimalMin,DecimalMax} from '@vaadin/form';
import {Email,Null,NotNull,NotEmpty,NotBlank,AssertTrue,AssertFalse,Negative,NegativeOrZero,Positive,PositiveOrZero,Size,Past,Future,Digits,Min,Max,Pattern,DecimalMin,DecimalMax} from '@vaadin/form';

/**
* This module is generated from com.vaadin.flow.server.connect.generator.tsmodel.TsFormEndpoint.MyEntity.
Expand All @@ -28,7 +28,6 @@ export default class MyEntityModel<T extends MyEntity = MyEntity> extends MyEnti
public readonly entityMatrix = new ArrayModel<ModelType<ArrayModel<ModelType<MyEntityModel>, MyEntityModel>>, ArrayModel<ModelType<MyEntityModel>, MyEntityModel>>(this, 'entityMatrix', ArrayModel, [MyEntityModel, []]);
public readonly foo = new StringModel(this, 'foo');
public readonly future = new StringModel(this, 'future', new Future());
public readonly futureOrPresent = new ObjectModel(this, 'futureOrPresent', new FutureOrPresent());
public readonly isNull = new StringModel(this, 'isNull', new Null());
public readonly list = new ArrayModel(this, 'list', StringModel, [], new NotEmpty());
public readonly max = new NumberModel(this, 'max', new Max(2));
Expand All @@ -40,7 +39,6 @@ export default class MyEntityModel<T extends MyEntity = MyEntity> extends MyEnti
public readonly notNull = new StringModel(this, 'notNull', new NotNull());
public readonly numberMatrix = new ArrayModel(this, 'numberMatrix', ArrayModel, [NumberModel, []]);
public readonly past = new StringModel(this, 'past', new Past());
public readonly pastOrPresent = new ObjectModel(this, 'pastOrPresent', new PastOrPresent());
public readonly pattern = new StringModel(this, 'pattern', new Pattern({regexp:"\\d+\\..+"}));
public readonly positive = new NumberModel(this, 'positive', new Positive());
public readonly positiveOrCero = new NumberModel(this, 'positiveOrCero', new PositiveOrZero());
Expand Down

0 comments on commit f987bb9

Please sign in to comment.