Skip to content

Commit

Permalink
Merge pull request #69 from SentryMan/valid
Browse files Browse the repository at this point in the history
Optional Support/AOP Method Return Types
  • Loading branch information
rob-bygrave authored Jul 10, 2023
2 parents 36e1f87 + 2470149 commit e04978a
Show file tree
Hide file tree
Showing 45 changed files with 404 additions and 314 deletions.
10 changes: 6 additions & 4 deletions blackbox-test/src/main/java/example/avaje/method/MethodTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

import java.util.List;

import io.avaje.validation.inject.aspect.ValidateParams;
import io.avaje.validation.inject.aspect.ValidateMethod;
import jakarta.inject.Singleton;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;

@Singleton
public class MethodTest {

@ValidateParams
void test(@NotEmpty List<@NotNull String> str, @Positive int inty, String regular) {}
@NotNull
@ValidateMethod
String test(@NotEmpty List<@NotNull String> str, @Positive int inty, String regular) {
return regular;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package example.avaje.optional;

import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;

import io.avaje.validation.constraints.NotBlank;
import io.avaje.validation.constraints.Positive;
import io.avaje.validation.constraints.Valid;

@Valid
public record CurseBearer(
@NotBlank(message = "it'll happen to you too") Optional<String> name,
@Positive OptionalInt estus,
@Positive(message = "You Died") OptionalLong souls,
@Positive(message = "you didn't pass the vigor check") OptionalDouble vigor) {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import java.util.List;
import java.util.Map;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.NotBlank;
import io.avaje.validation.constraints.NotNull;
import io.avaje.validation.constraints.Valid;

@Valid
public record Ship(
Expand Down
2 changes: 1 addition & 1 deletion blackbox-test/src/test/java/example/avaje/AMyEmail.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package example.avaje;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.Email;
import io.avaje.validation.constraints.Valid;

@Valid
public class AMyEmail {
Expand Down
2 changes: 1 addition & 1 deletion blackbox-test/src/test/java/example/avaje/AMyNumbers.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package example.avaje;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.DecimalMax;
import io.avaje.validation.constraints.Valid;

import java.math.BigDecimal;

Expand Down
2 changes: 1 addition & 1 deletion blackbox-test/src/test/java/example/avaje/AMyPattern.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package example.avaje;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.Pattern;
import io.avaje.validation.constraints.Valid;

@Valid
public class AMyPattern {
Expand Down
1 change: 0 additions & 1 deletion blackbox-test/src/test/java/example/avaje/ANums.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package example.avaje;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.*;

import java.math.BigDecimal;
Expand Down
1 change: 0 additions & 1 deletion blackbox-test/src/test/java/example/avaje/APastFuture.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package example.avaje;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.*;

import java.time.LocalDate;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package example.avaje.method;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;

import java.util.List;

Expand All @@ -18,12 +19,19 @@ class MethodTestTest {

@Test
void test() {
assertThatNoException().isThrownBy(() -> proxy.test(List.of(""), 1, null));
assertThatNoException().isThrownBy(() -> proxy.test(List.of(""), 1, "result"));
}

@Test
void invalid() {
assertThatThrownBy(() -> proxy.test(null, 0, null))
.isInstanceOf(ConstraintViolationException.class);
try {

proxy.test(List.of(), 0, null);
fail("how???");
} catch (final ConstraintViolationException e) {
final var violations = e.violations();

assertThat(violations).hasSize(3);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package example.avaje.nested;

import io.avaje.validation.Valid;
import io.avaje.validation.constraints.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package example.avaje.optional;

import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

import example.avaje.composable.Sans;
import io.avaje.validation.ConstraintViolation;
import io.avaje.validation.ConstraintViolationException;
import io.avaje.validation.Validator;

class OptionalTest {

final Validator validator = Validator.builder().build();

@Test
void valid() {
final var monarch =
new CurseBearer(
Optional.of("Belmont"),
OptionalInt.of(5),
OptionalLong.of(10000),
OptionalDouble.of(42.0));
validator.validate(monarch);
}

@Test
void validEmpty() {
final var hollow =
new CurseBearer(
Optional.empty(), OptionalInt.empty(), OptionalLong.empty(), OptionalDouble.empty());
validator.validate(hollow);
}

@Test
void validNull() {
final var hollow = new CurseBearer(null, null, null, null);
validator.validate(hollow);
}

@Test
void invalid() {
final var violations =
violations(
new CurseBearer(
Optional.of(""), OptionalInt.of(0), OptionalLong.of(0), OptionalDouble.of(0)));
assertThat(violations)
.contains(
"it'll happen to you too",
"must be greater than 0",
"You Died",
"you didn't pass the vigor check");
}

Set<String> violations(Object any) {
try {
validator.validate(any);
fail("not expected");
return null;
} catch (final ConstraintViolationException e) {
return e.violations().stream().map(ConstraintViolation::message).collect(toSet());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.avaje.validation;
package io.avaje.validation.constraints;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
Expand All @@ -15,13 +15,5 @@

/**
*/
@Retention(CLASS)
@Target({TYPE, PACKAGE})
@interface Import {

/**
* Specify types to generate Valid Adapters for.
*/
Class<?>[] value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package io.avaje.validation.generator;

import java.util.Map;
import static io.avaje.validation.generator.ProcessingContext.isAssignable2Interface;

public class AdapterHelper {

static void writeAdapterWithValues(
Append writer, ElementAnnotationContainer elementAnnotations, String indent, String type) {
boolean first = true;
final var annotations = elementAnnotations.annotations();
final var genericType = elementAnnotations.genericType();
final var typeUse1 = elementAnnotations.typeUse1();
final var typeUse2 = elementAnnotations.typeUse2();
final var hasValid = elementAnnotations.hasValid();
for (final var a : annotations.entrySet()) {
if (first) {
writer.append(
"%sctx.<%s>adapter(%s.class, %s)", indent, type, a.getKey().shortName(), a.getValue());
first = false;
continue;
}
writer
.eol()
.append(
"%s .andThen(ctx.adapter(%s.class,%s))",
indent, a.getKey().shortName(), a.getValue());
}

if (annotations.isEmpty()) {
writer.append("%sctx.<%s>noop()", indent, type);
}

if (!typeUse1.isEmpty()
&& (isAssignable2Interface(genericType.topType(), "java.lang.Iterable"))) {
writer.eol().append("%s .list()", indent);
final var t = genericType.firstParamType();
writeTypeUse(writer, indent, t, typeUse1, genericType);

} else if ((!typeUse1.isEmpty() || !typeUse2.isEmpty())
&& "java.util.Map".equals(genericType.topType())) {

writer.eol().append("%s .mapKeys()", indent);
writeTypeUse(writer, indent, genericType.firstParamType(), typeUse1, genericType);

writer.eol().append("%s .mapValues()", indent);
writeTypeUse(writer, indent, genericType.secondParamType(), typeUse2, false, genericType);

} else if (genericType.topType().contains("[]") && hasValid) {

writer.eol().append("%s .array()", indent);
writeTypeUse(writer, indent, genericType.firstParamType(), typeUse1, genericType);
} else if (hasValid) {
writer
.eol()
.append(
"%s .andThen(ctx.adapter(%s.class))",
indent, Util.shortName(genericType.topType()));
} else if (genericType.topType().contains("java.util.Optional")) {
writer.eol().append("%s .optional()", indent);
}
}

private static void writeTypeUse(
Append writer,
String indent,
String firstParamType,
Map<GenericType, String> typeUse12,
GenericType genericType) {
writeTypeUse(writer, indent, firstParamType, typeUse12, true, genericType);
}

private static void writeTypeUse(
Append writer,
String indent,
String paramType,
Map<GenericType, String> typeUseMap,
boolean keys,
GenericType genericType) {

for (final var a : typeUseMap.entrySet()) {

if (Constants.VALID_ANNOTATIONS.contains(a.getKey().topType())) {
continue;
}
final var k = a.getKey().shortName();
final var v = a.getValue();
writer.eol().append("%s .andThenMulti(ctx.adapter(%s.class,%s))", indent, k, v);
}

if (!Util.isBasicType(paramType)
&& typeUseMap.keySet().stream()
.map(GenericType::topType)
.anyMatch(Constants.VALID_ANNOTATIONS::contains)) {

writer
.eol()
.append(
"%s .andThenMulti(ctx.adapter(%s.class))",
indent,
Util.shortName(keys ? genericType.firstParamType() : genericType.secondParamType()));
}
}
}
Loading

0 comments on commit e04978a

Please sign in to comment.