Skip to content

Commit

Permalink
fix: allow negative and zero time element
Browse files Browse the repository at this point in the history
Passes validation for duration strings with zero or negative durations.
  • Loading branch information
memdal committed Jan 2, 2024
1 parent 781e0fb commit 9b853e3
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 22 deletions.
16 changes: 9 additions & 7 deletions src/main/java/io/vertx/json/schema/impl/Format.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,19 @@ private static boolean testUri(String value) {
return NOT_URI_FRAGMENT.matcher(value).find() && URI_PATTERN.matcher(value).find();
}

private static final Pattern DURATION_A = Pattern.compile("^-?P\\d+([.,]\\d+)?W$");
private static final Pattern DURATIION_B = Pattern.compile("^-?P[\\dYMDTHS]*(\\d[.,]\\d+)?[YMDHS]$");
private static final Pattern DURATIION_C = Pattern.compile("^-?P([.,\\d]+Y)?([.,\\d]+M)?([.,\\d]+D)?(T([.,\\d]+H)?([.,\\d]+M)?([.,\\d]+S)?)?$");
// Matches to true if a duration string is not a zero duration with a minus sign in front.
private static final Pattern DURATION_D = Pattern.compile("^(-.*[1-9].*|[^-].*)$");

private static final Pattern DURATION_A = Pattern.compile("^P\\d+([.,]\\d+)?W$");
private static final Pattern DURATIION_B = Pattern.compile("^P[\\dYMDTHS]*(\\d[.,]\\d+)?[YMDHS]$");
private static final Pattern DURATIION_C = Pattern.compile("^P([.,\\d]+Y)?([.,\\d]+M)?([.,\\d]+D)?(T([.,\\d]+H)?([.,\\d]+M)?([.,\\d]+S)?)?$");
private static final Pattern DURATION_D = Pattern.compile("^-?P(-?\\d+(\\.\\d+)?Y)?(-?\\d+(\\.\\d+)?M)?(-?\\d+(\\.\\d+)?W)?(-?\\d+(\\.\\d+)?D)?(T(-?\\d+(\\.\\d+)?H)?(-?\\d+(\\.\\d+)?M)?(-?\\d+(\\.\\d+)?S)?)?$");

private static boolean testDuration(String value) {
return value.length() > 1 &&
value.length() < 80 &&
(DURATION_A.matcher(value).find() || (DURATIION_B.matcher(value).find() && DURATIION_C.matcher(value).find())) &&
(DURATION_D.matcher(value).find());
(DURATION_A.matcher(value).find() ||
(DURATIION_B.matcher(value).find() &&
DURATIION_C.matcher(value).find())) ||
DURATION_D.matcher(value).find();
}

private static final Pattern FASTDATETIME = Pattern.compile("^\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d[t\\s](?:[0-2]\\d:[0-5]\\d:[0-5]\\d|23:59:60)(?:\\.\\d+)?(?:z|[+-]\\d\\d(?::?\\d\\d)?)$", Pattern.CASE_INSENSITIVE);
Expand Down
56 changes: 41 additions & 15 deletions src/test/java/io/vertx/json/schema/ValidationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import java.util.function.Consumer;

@ExtendWith(VertxExtension.class)
class ValidationTest {

Expand Down Expand Up @@ -226,7 +228,7 @@ public void testIssue49b() throws JsonSchemaValidationException {
}

@Test
void testNegativeDurationValidation() throws JsonSchemaValidationException {
void testDurationValidation() {

SchemaRepository repository = SchemaRepository.create(new JsonSchemaOptions().setDraft(Draft.DRAFT201909).setBaseUri("app://"));

Expand All @@ -246,19 +248,43 @@ void testNegativeDurationValidation() throws JsonSchemaValidationException {
"}")
));

JsonObject positiveDuration = new JsonObject().put("duration", "P3W");
JsonObject negativeDuration = new JsonObject().put("duration", "-P3W");
JsonObject positiveNullDuration = new JsonObject().put("duration", "P0W");
JsonObject negativeNullDuration = new JsonObject().put("duration", "-PT0S");

repository.validator("acceptable-duration.json").validate(positiveDuration).checkValidity();
repository.validator("acceptable-duration.json").validate(negativeDuration).checkValidity();
repository.validator("acceptable-duration.json").validate(positiveNullDuration).checkValidity();
try {
repository.validator("acceptable-duration.json").validate(negativeNullDuration).checkValidity();
fail("Should have thrown an exception");
} catch (JsonSchemaValidationException e) {
// OK
}
Consumer<String> shouldValidate = duration -> {
try {
repository.validator("acceptable-duration.json").validate(new JsonObject().put("duration", duration)).checkValidity();
} catch (JsonSchemaValidationException ignored) {
fail("Duration " + duration + " should pass validation.");
}
};

Consumer<String> shouldNotValidate = duration -> {
try {
repository.validator("acceptable-duration.json").validate(new JsonObject().put("duration", duration)).checkValidity();
fail("Duration " + duration + " should not pass validation.");
} catch (JsonSchemaValidationException ignored) {}
};

shouldValidate.accept("PT0S");
shouldValidate.accept("-PT0S");
shouldValidate.accept("PT-0S");
shouldValidate.accept("P1Y");
shouldValidate.accept("P1M");
shouldValidate.accept("P1W");
shouldValidate.accept("P1D");
shouldValidate.accept("PT1H");
shouldValidate.accept("PT1M");
shouldValidate.accept("PT1S");
shouldValidate.accept("P1Y2M");
shouldValidate.accept("P1M2W");
shouldValidate.accept("P1W2D");
shouldNotValidate.accept("P1M2Y");
shouldNotValidate.accept("P1W2M");
shouldNotValidate.accept("P1D2W");
shouldValidate.accept("PT1H2M");
shouldValidate.accept("PT1M2S");
shouldNotValidate.accept("PT1M2H");
shouldNotValidate.accept("PT1S2M");
shouldValidate.accept("P1Y2M3W4DT5H6M7S");
shouldValidate.accept("P-1Y-2M-3W-4DT-5H-6M-7S");
shouldValidate.accept("-P-1.0Y2.2M-33.4W-4.2DT5H-7S");
}
}

0 comments on commit 9b853e3

Please sign in to comment.