Skip to content

Commit

Permalink
✨ (#10): Add delegates to validate new Verein and Owner
Browse files Browse the repository at this point in the history
  • Loading branch information
d135-1r43 committed Aug 25, 2024
1 parent fa4f1a5 commit a4337af
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package app.hopps.vereine.delegates;

import app.hopps.vereine.jpa.Mitglied;
import app.hopps.vereine.jpa.MitgliedRespository;
import app.hopps.vereine.jpa.Verein;
import app.hopps.vereine.jpa.VereinRepository;
import app.hopps.vereine.validation.NonUniqueConstraintViolation;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;

import java.util.HashSet;
import java.util.Set;

@ApplicationScoped
public class CreationValidationDelegate {

@Inject
Validator validator;

@Inject
MitgliedRespository mitgliedRespository;

@Inject
VereinRepository vereinRepository;

public void validateWithValidator(Verein verein, Mitglied owner) {

Set<ConstraintViolation<?>> violations = new HashSet<>();
violations.addAll(validator.validate(verein));
violations.addAll(validator.validate(owner));

if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}

public void validateUniqueness(Verein verein, Mitglied owner) throws NonUniqueConstraintViolation.NonUniqueConstraintViolationException {

// Not having a proper Jakarta Validator is intentional, as a Hibernate Proxy might be valid, although its content
// is already in the database, ergo not unique

Set<NonUniqueConstraintViolation> nonUniqueConstraintViolations = new HashSet<>();
boolean ownerUnique = (mitgliedRespository.findByEmail(owner.getEmail()) == null);
if (!ownerUnique) {
nonUniqueConstraintViolations.add(new NonUniqueConstraintViolation("email", owner));
}

boolean vereinUnique = (vereinRepository.findBySlug(verein.getSlug()) == null);
if (!vereinUnique) {
nonUniqueConstraintViolations.add(new NonUniqueConstraintViolation("slug", verein));
}

if (!nonUniqueConstraintViolations.isEmpty()) {
throw new NonUniqueConstraintViolation.NonUniqueConstraintViolationException(nonUniqueConstraintViolations);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

@Embeddable
public class Address {

private String street;
private String number;
private String city;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@

@ApplicationScoped
public class MitgliedRespository implements PanacheRepository<Mitglied> {
// auto implemented

public Mitglied findByEmail(String email) {
return find("email", email).firstResult();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package app.hopps.vereine.jpa;

import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class VereinRepository implements PanacheRepository<Verein> {

// auto implemented
public Verein findBySlug(String slug) {
return find("slug", slug).firstResult();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package app.hopps.vereine.validation;

import java.util.Collections;
import java.util.Set;

public record NonUniqueConstraintViolation(String field, Object root) {

public String getMessage() {
return "must be unique";
}

public static final class NonUniqueConstraintViolationException extends Exception {

private final Set<NonUniqueConstraintViolation> violations;

public NonUniqueConstraintViolationException(Set<NonUniqueConstraintViolation> violations) {
super("not unique");
this.violations = violations;
}

public Set<NonUniqueConstraintViolation> getViolations() {
return Collections.unmodifiableSet(violations);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package app.hopps.vereine.delegates;

import app.hopps.vereine.jpa.Mitglied;
import app.hopps.vereine.jpa.MitgliedRespository;
import app.hopps.vereine.jpa.Verein;
import app.hopps.vereine.jpa.VereinRepository;
import app.hopps.vereine.validation.NonUniqueConstraintViolation;
import app.hopps.vereine.validation.NonUniqueConstraintViolation.NonUniqueConstraintViolationException;
import io.quarkus.narayana.jta.QuarkusTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.ConstraintViolationException;
import org.hamcrest.core.IsInstanceOf;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

@QuarkusTest
public class CreationValidationDelegateTests {

@Inject
CreationValidationDelegate delegate;

@Inject
VereinRepository vereinRepository;

@Inject
MitgliedRespository mitgliedRespository;

@BeforeEach
@Transactional
void setUp() {

vereinRepository.deleteAll();
mitgliedRespository.deleteAll();
}

@Test
@DisplayName("should validate valid data")
void shouldValidateWithJakartaValidator() {

// given
Verein verein = new Verein();
verein.setName("Kegelclub 777");
verein.setType(Verein.TYPE.EINGETRAGENER_VEREIN);
verein.setSlug("kegelclub-777");

Mitglied owner = new Mitglied();
owner.setFirstName("Kevin");
owner.setLastName("Kegler");
owner.setEmail("[email protected]");

verein.getMitglieder().add(owner);

// when
delegate.validateWithValidator(verein, owner);

// then no exception
}

@Test
@DisplayName("should invalidate invalid data")
void shouldInvalidateWithJakartaValidator() {

// given
Verein verein = new Verein();
verein.setName("Kegelclub 777");
verein.setType(Verein.TYPE.EINGETRAGENER_VEREIN);
verein.setSlug("kegelclub-777-");

Mitglied owner = new Mitglied();
owner.setFirstName("Kevin");
owner.setLastName("Kegler");
owner.setEmail("[email protected]");

verein.getMitglieder().add(owner);

// when
assertThrows(ConstraintViolationException.class, () -> delegate.validateWithValidator(verein, owner));
}

@Test
@DisplayName("should validate valid data")
void shouldValidateUniqueness() throws NonUniqueConstraintViolationException {

// given empty database
Verein verein = new Verein();
verein.setName("Kegelclub 777");
verein.setType(Verein.TYPE.EINGETRAGENER_VEREIN);
verein.setSlug("kegelclub-777");

Mitglied owner = new Mitglied();
owner.setFirstName("Kevin");
owner.setLastName("Kegler");
owner.setEmail("[email protected]");

verein.getMitglieder().add(owner);

// when
delegate.validateUniqueness(verein, owner);

// then no exception
}

@Test
@DisplayName("should validate valid data")
void shouldInvalidateUniqueness() throws NonUniqueConstraintViolationException {

// given
Verein existingVerein = new Verein();
existingVerein.setName("Kegelclub 777");
existingVerein.setType(Verein.TYPE.EINGETRAGENER_VEREIN);
existingVerein.setSlug("kegelclub-777");

QuarkusTransaction.begin();
vereinRepository.persist(existingVerein);
QuarkusTransaction.commit();

// when
Verein verein = new Verein();
verein.setName("Kegelclub 777");
verein.setType(Verein.TYPE.EINGETRAGENER_VEREIN);
verein.setSlug("kegelclub-777");

Mitglied owner = new Mitglied();
owner.setFirstName("Kevin");
owner.setLastName("Kegler");
owner.setEmail("[email protected]");

verein.getMitglieder().add(owner);


// then
NonUniqueConstraintViolationException exception = assertThrows(NonUniqueConstraintViolationException.class, () -> delegate.validateUniqueness(verein, owner));
assertThat(exception.getViolations(), hasSize(1));

NonUniqueConstraintViolation onlyViolation = exception.getViolations().stream().findFirst().orElseThrow();
assertEquals("slug", onlyViolation.field());
assertThat(onlyViolation.getMessage(), is("must be unique"));
assertThat(onlyViolation.root(), IsInstanceOf.instanceOf(Verein.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ void shouldValidate() {
// given
Verein verein = Instancio.create(Verein.class);
verein.setId(null);
verein.setSlug("foobar");

// when
ValidationResult validationResult = RestValidator.forCandidate(verein)
Expand All @@ -47,6 +48,7 @@ void shouldInvalidate() {
Verein verein = Instancio.create(Verein.class);
verein.setId(null);
verein.setName("");
verein.setSlug("foobar");

// when
ValidationResult validationResult = RestValidator.forCandidate(verein)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ void shouldValidateValidVerein() {

// given
Verein verein = Instancio.create(Verein.class);
verein.setSlug("foobar");
verein.setId(null);

RestAssured.given()
Expand All @@ -38,6 +39,7 @@ void shouldInvalidateVereinWithoutName() {

// given
Verein verein = Instancio.create(Verein.class);
verein.setSlug("foobar");
verein.setId(null);
verein.setName("");

Expand Down

0 comments on commit a4337af

Please sign in to comment.