Skip to content

Commit

Permalink
Custom validation have incomplete proprety path (#450)
Browse files Browse the repository at this point in the history
* Custom validation have incomplete proprety path

Reproducer for #205

* Fix path and delombok

---------

Co-authored-by: Denis Stepanov <[email protected]>
  • Loading branch information
altro3 and dstepanov authored Jan 17, 2025
1 parent 0a41c5a commit d1c58f4
Show file tree
Hide file tree
Showing 28 changed files with 2,165 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.micronaut.docs.validation.path;

import io.micronaut.docs.validation.path.model.Dag;
import io.micronaut.docs.validation.path.model.Flow;
import io.micronaut.docs.validation.path.model.Log;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import jakarta.validation.Validator;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

@MicronautTest(startApplication = false)
public class ValidationTest {
@Inject
Validator validator;

@Test
void testValidationOk() {
Flow f = Flow.builder()
.id("id")
.namespace("namespace")
.tasks(List.of(Log.builder().id("task").type(Log.class.getName()).message("").build()))
.build();

var violation = validator.validate(f).stream().findFirst().get();
assertEquals("tasks[0].message", violation.getPropertyPath().toString());
assertEquals("must not be blank", violation.getMessage());
}

@Test
void testValidationKo() {
Flow f = Flow.builder()
.id("id")
.namespace("namespace")
.tasks(List.of(
Dag.builder()
.id("dag")
.type(Dag.class.getName())
.dagTasks(List.of(
Dag.DagTask.builder()
.task(Log.builder()
.id("cycle")
.type(Log.class.getName())
.message("")
.build())
.dependsOn(List.of("cycle"))
.build()
))
.build())
)
.build();

var violation = validator.validate(f).stream().findFirst().get();
assertEquals("tasks[0].dagTasks", violation.getPropertyPath().toString());
assertEquals("Cyclic dependency detected: cycle", violation.getMessage());
}

@Test
void testRootError() {
Flow f = Flow.builder()
.id("id")
.namespace("namespace")
.tasks(List.of(
Dag.builder()
.id("dag")
.type(Dag.class.getName())
.dagTasks(List.of(
Dag.DagTask.builder()
.task(Log.builder()
.id("cycle")
.type(Log.class.getName())
.message("")
.build())
.dependsOn(List.of("xyz"))
.build()
))
.build())
)
.build();

var violation = validator.validate(f).stream().findFirst().get();
assertEquals("tasks[0]", violation.getPropertyPath().toString());
assertEquals("Not existing task id in dependency: xyz", violation.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package io.micronaut.docs.validation.path.model;

import io.micronaut.core.annotation.Introspected;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

import java.util.List;

@Introspected
abstract public class AbstractTrigger {
@NotNull
@NotBlank
@Pattern(regexp = "[a-zA-Z0-9_-]+")
protected String id;

@NotNull
@NotBlank
@Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*")
protected String type;

private String description;

@Valid
private List<Condition> conditions;

@NotNull
private boolean disabled = false;

@Valid
private WorkerGroup workerGroup;

public AbstractTrigger() {
}

protected AbstractTrigger(AbstractTriggerBuilder<?, ?> b) {
this.id = b.id;
this.type = b.type;
this.description = b.description;
this.conditions = b.conditions;
if (b.disabled$set) {
this.disabled = b.disabled$value;
} else {
this.disabled = $default$disabled();
}
this.workerGroup = b.workerGroup;
}

private static boolean $default$disabled() {
return false;
}

public @NotNull @NotBlank @Pattern(regexp = "[a-zA-Z0-9_-]+") String getId() {
return this.id;
}

public @NotNull @NotBlank @Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String getType() {
return this.type;
}

public String getDescription() {
return this.description;
}

public @Valid List<Condition> getConditions() {
return this.conditions;
}

public @NotNull boolean isDisabled() {
return this.disabled;
}

public @Valid WorkerGroup getWorkerGroup() {
return this.workerGroup;
}

public static abstract class AbstractTriggerBuilder<C extends AbstractTrigger, B extends AbstractTriggerBuilder<C, B>> {
private @NotNull
@NotBlank
@Pattern(regexp = "[a-zA-Z0-9_-]+") String id;
private @NotNull
@NotBlank
@Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String type;
private String description;
private @Valid List<Condition> conditions;
private @NotNull boolean disabled$value;
private boolean disabled$set;
private @Valid WorkerGroup workerGroup;

public B id(@NotNull @NotBlank @Pattern(regexp = "[a-zA-Z0-9_-]+") String id) {
this.id = id;
return self();
}

public B type(@NotNull @NotBlank @Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String type) {
this.type = type;
return self();
}

public B description(String description) {
this.description = description;
return self();
}

public B conditions(@Valid List<Condition> conditions) {
this.conditions = conditions;
return self();
}

public B disabled(@NotNull boolean disabled) {
this.disabled$value = disabled;
this.disabled$set = true;
return self();
}

public B workerGroup(@Valid WorkerGroup workerGroup) {
this.workerGroup = workerGroup;
return self();
}

protected abstract B self();

public abstract C build();

public String toString() {
return "AbstractTrigger.AbstractTriggerBuilder(id=" + this.id + ", type=" + this.type + ", description=" + this.description + ", conditions=" + this.conditions + ", disabled$value=" + this.disabled$value + ", workerGroup=" + this.workerGroup + ")";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.micronaut.docs.validation.path.model;

import io.micronaut.core.annotation.Introspected;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

@Introspected
public abstract class Condition {
@NotNull
@Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*")
protected String type;

public Condition(@NotNull @Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String type) {
this.type = type;
}

public Condition() {
}

public @NotNull @Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String getType() {
return this.type;
}

public static abstract class ConditionBuilder<C extends Condition, B extends ConditionBuilder<C, B>> {
private @NotNull
@Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String type;

public B type(@NotNull @Pattern(regexp = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*") String type) {
this.type = type;
return self();
}

protected abstract B self();

public abstract C build();

public String toString() {
return "Condition.ConditionBuilder(type=" + this.type + ")";
}
}
}
Loading

0 comments on commit d1c58f4

Please sign in to comment.