Skip to content

Commit

Permalink
Merge pull request #336 from devgateway/task/3.x/unique-property-vali…
Browse files Browse the repository at this point in the history
…dator

unique property validator (3.x)
  • Loading branch information
mpostelnicu authored Nov 23, 2020
2 parents 052b271 + 304dd62 commit 19f5047
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.devgateway.toolkit.forms.validators;

import org.apache.wicket.Component;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
import org.devgateway.toolkit.persistence.dao.GenericPersistable;
import org.devgateway.toolkit.persistence.service.UniquePropertyService;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;

/**
* @author Nadejda Mandrescu
*/
public class UniquePropertyValidator<T extends GenericPersistable & Serializable, V> implements IValidator<V> {
private static final long serialVersionUID = -7185120038483258730L;

protected final UniquePropertyService<T> uniquePropertyService;
protected final Collection<Long> entityIdsToIgnore;
protected final String propertyName;
protected final IModel<String> propertyLabel;

public UniquePropertyValidator(final UniquePropertyService<T> uniquePropertyService,
final long entityIdToIgnore, final String propertyName, final Component component) {
this(uniquePropertyService, Collections.singleton(entityIdToIgnore), propertyName, component);
}

public UniquePropertyValidator(final UniquePropertyService<T> uniquePropertyService,
final Collection<Long> entityIdsToIgnore, final String propertyName, final Component component) {
this(uniquePropertyService, entityIdsToIgnore, propertyName,
new StringResourceModel(propertyName + ".label", component));
}

public UniquePropertyValidator(final UniquePropertyService<T> uniquePropertyService,
final Collection<Long> entityIdsToIgnore, final String propertyName, final IModel<String> propertyLabel) {
if (entityIdsToIgnore.isEmpty()) {
entityIdsToIgnore.add(-1L);
}
this.uniquePropertyService = uniquePropertyService;
this.entityIdsToIgnore = entityIdsToIgnore;
this.propertyName = propertyName;
this.propertyLabel = propertyLabel;
}

@Override
public void validate(final IValidatable<V> validatable) {
final V value = validatable.getValue();
if (uniquePropertyService.existsByProperty(propertyName, value, entityIdsToIgnore)) {
ValidationError error = new ValidationError(this);
error.setVariable("label", propertyLabel.getObject());
error.setVariable("input", value);
validatable.error(error);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UniquePropertyValidator=${label} equal to '${input}' already exists
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.lang.Objects;
import org.devgateway.toolkit.forms.security.SecurityConstants;
import org.devgateway.toolkit.forms.validators.UniquePropertyValidator;
import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxBootstrapFormComponent;
import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxPickerBootstrapFormComponent;
import org.devgateway.toolkit.forms.wicket.components.form.CheckBoxToggleBootstrapFormComponent;
Expand Down Expand Up @@ -77,6 +79,8 @@ protected void onInitialize() {
TextFieldBootstrapFormComponent<String> textField = new TextFieldBootstrapFormComponent<>("textField");
editForm.add(textField);
textField.required();
textField.getField().add(new UniquePropertyValidator<>(testFormService, Objects.defaultIfNull(entityId, -1L),
"textField", this));
textField.enableRevisionsView();


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@

import org.devgateway.toolkit.persistence.dao.TestForm;
import org.devgateway.toolkit.persistence.repository.norepository.BaseJpaRepository;
import org.devgateway.toolkit.persistence.repository.norepository.UniquePropertyRepository;
import org.springframework.transaction.annotation.Transactional;

/**
* @author mpostelnicu
*/
@Transactional
public interface TestFormRepository extends BaseJpaRepository<TestForm, Long> {
public interface TestFormRepository extends BaseJpaRepository<TestForm, Long>,
UniquePropertyRepository<TestForm, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.devgateway.toolkit.persistence.repository.norepository;

import org.apache.commons.lang3.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

/**
* @author Nadejda Mandrescu
*/
public final class SpecificationUtils {
private SpecificationUtils() {
}

public static Expression<String> ignoreCaseLikeValue(final CriteriaBuilder cb, final String value) {
return cb.lower(cb.concat(cb.concat(cb.literal("%"), value), "%"));
}

public static Predicate equalIgnoreCaseValue(final Root<?> root, final CriteriaBuilder cb,
final String propertyName, final String value) {
return cb.equal(cb.lower(root.get(propertyName)), StringUtils.lowerCase(value));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.devgateway.toolkit.persistence.repository.norepository;

import org.devgateway.toolkit.persistence.dao.GenericPersistable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* @author Nadejda Mandrescu
*/
@NoRepositoryBean
@Transactional
public interface UniquePropertyRepository<T extends GenericPersistable, ID extends Serializable>
extends BaseJpaRepository<T, ID> {

default Specification<T> getFindByPropertySpecification(final String propertyName, final Object propertyValue,
final Collection<ID> ignoreIds) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
Path<?> propertyPath = root.get(propertyName);
if (String.class.isAssignableFrom(propertyPath.getJavaType())) {
predicates.add(SpecificationUtils.equalIgnoreCaseValue(root, cb, propertyName, (String) propertyValue));
} else {
predicates.add(cb.equal(propertyPath, propertyValue));
}
predicates.add(root.get("id").in(ignoreIds).not());
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
};

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

import org.devgateway.toolkit.persistence.dao.TestForm;

public interface TestFormService extends BaseJpaService<TestForm> {
public interface TestFormService extends BaseJpaService<TestForm>, UniquePropertyService<TestForm> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.devgateway.toolkit.persistence.dao.TestForm;
import org.devgateway.toolkit.persistence.repository.TestFormRepository;
import org.devgateway.toolkit.persistence.repository.norepository.BaseJpaRepository;
import org.devgateway.toolkit.persistence.repository.norepository.UniquePropertyRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
Expand All @@ -23,6 +24,11 @@ protected BaseJpaRepository<TestForm, Long> repository() {
return testFormRepository;
}

@Override
public UniquePropertyRepository<TestForm, Long> uniquePropertyRepository() {
return testFormRepository;
}

@Override
public TestForm newInstance() {
return new TestForm();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.devgateway.toolkit.persistence.service;

import org.devgateway.toolkit.persistence.dao.GenericPersistable;
import org.devgateway.toolkit.persistence.repository.norepository.UniquePropertyRepository;
import org.springframework.data.jpa.domain.Specification;

import java.io.Serializable;
import java.util.Collection;

/**
* @author Nadejda Mandrescu
*/
public interface UniquePropertyService<T extends GenericPersistable & Serializable> {
UniquePropertyRepository<T, Long> uniquePropertyRepository();

default T findByProperty(final String propertyName, final Object propertyValue, final Collection<Long> ignoreIds) {
final Specification<T> spec =
uniquePropertyRepository().getFindByPropertySpecification(propertyName, propertyValue, ignoreIds);

return uniquePropertyRepository().findOne(spec).orElse(null);
}

default boolean existsByProperty(final String propertyName, final Object propertyValue,
final Collection<Long> ignoreIds) {
final Specification<T> spec =
uniquePropertyRepository().getFindByPropertySpecification(propertyName, propertyValue, ignoreIds);

return uniquePropertyRepository().count(spec) > 0;
}

}

0 comments on commit 19f5047

Please sign in to comment.