Skip to content

Commit

Permalink
Merge branch 'dev' into 34498_implement_RepositoryV2_update_operation
Browse files Browse the repository at this point in the history
  • Loading branch information
cgendreau committed Jan 10, 2025
2 parents d3c4484 + b186478 commit 1ff548c
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ca.gc.aafc.dina.entity;

import java.util.List;

import ca.gc.aafc.dina.vocabulary.VocabularyElement;

public interface IdentifierType extends VocabularyElement, DinaEntity {

/**
* The key should not contain dot(.) See {@link VocabularyElement#getKey()}
* @param key
*/
void setKey(String key);

/**
* The component (material-sample, project) where this identifier type is expected to be used.
*/
List<String> getDinaComponents();

/**
* Like wikidata. A URI template where "$1" can be automatically replaced with the value
* assigned to the identifier.
*/
String getUriTemplate();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ca.gc.aafc.dina.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.SmartValidator;

import ca.gc.aafc.dina.entity.IdentifierType;
import ca.gc.aafc.dina.jpa.BaseDAO;
import ca.gc.aafc.dina.validation.IdentifierTypeValidator;
import ca.gc.aafc.dina.vocabulary.VocabularyKeyHelper;

import lombok.NonNull;

/**
* Service to handle {@link IdentifierType}
* @param <T>
*/
public class IdentifierTypeService<T extends IdentifierType> extends DefaultDinaService<T> {

private final IdentifierTypeValidator identifierTypeValidator;

public IdentifierTypeService(@NonNull BaseDAO baseDAO,
@NonNull SmartValidator validator,
IdentifierTypeValidator identifierTypeValidator) {
super(baseDAO, validator);
this.identifierTypeValidator = identifierTypeValidator;
}

@Override
protected void preCreate(T entity) {
// Do we need to generate a key ?
if (StringUtils.isBlank(entity.getKey())) {
if (StringUtils.isNotBlank(entity.getName())) {
entity.setKey(VocabularyKeyHelper.generateKeyFromName(entity.getName()));
}
} else {
if (!VocabularyKeyHelper.isKeyValid(entity.getKey())) {
throw new IllegalArgumentException("Invalid key");
}
}
}

@Override
public void validateBusinessRules(T identifierType) {
applyBusinessRule(identifierType, identifierTypeValidator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,20 @@
import ca.gc.aafc.dina.entity.ManagedAttribute;
import ca.gc.aafc.dina.jpa.BaseDAO;
import ca.gc.aafc.dina.jpa.PredicateHelper;
import ca.gc.aafc.dina.vocabulary.VocabularyKeyHelper;

import lombok.NonNull;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.validation.SmartValidator;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
Expand All @@ -29,7 +27,6 @@
public abstract class ManagedAttributeService<T extends ManagedAttribute>
extends DefaultDinaService<T> {
private static final String KEY = "key";
private static final Pattern NON_ALPHANUMERICAL = Pattern.compile("[^a-z0-9]");
private final Class<T> maClass;

public ManagedAttributeService(BaseDAO baseDAO, SmartValidator smartValidator, @NonNull Class<T> managedAttributeClass) {
Expand All @@ -40,7 +37,7 @@ public ManagedAttributeService(BaseDAO baseDAO, SmartValidator smartValidator, @
@Override
protected void preCreate(T entity) {
if (StringUtils.isNotBlank(entity.getName())) {
entity.setKey(generateKeyFromName(entity.getName()));
entity.setKey(VocabularyKeyHelper.generateKeyFromName(entity.getName()));
}
}

Expand Down Expand Up @@ -86,26 +83,4 @@ public T findOneByKeyAnd(String key, Pair<String, Object> andClause) {
return findOneByProperties(maClass,
List.of(Pair.of(KEY, key), andClause));
}

/**
* Transforms a name into a key. camelCase is supported.
* "Aa bb !! mySuperAttribute # 11" will become aa_bb_my_super_attribute_11
* @param name
* @return
*/
private static String generateKeyFromName(String name) {
Objects.requireNonNull(name);

return Arrays.stream(StringUtils.
splitByCharacterTypeCamelCase(StringUtils.normalizeSpace(name)))
.filter(StringUtils::isNotBlank)
.map(ManagedAttributeService::processName)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining("_"));
}

private static String processName(String name) {
return RegExUtils.removeAll(name.toLowerCase(), NON_ALPHANUMERICAL);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ca.gc.aafc.dina.validation;

import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.MessageSource;
import org.springframework.validation.Errors;

import ca.gc.aafc.dina.entity.IdentifierType;

public class IdentifierTypeValidator extends DinaBaseValidator<IdentifierType> {

private static final String MISSING_PLACEHOLDER_KEY = "identifierType.uriTemplate.missingPlaceholder";

public IdentifierTypeValidator(@Named("validationMessageSource") MessageSource messageSource) {
super(IdentifierType.class, messageSource);
}

@Override
public void validateTarget(IdentifierType target, Errors errors) {

if (StringUtils.isNotBlank(target.getUriTemplate())) {
if (!target.getUriTemplate().contains("$1")) {
errors.reject(MISSING_PLACEHOLDER_KEY, getMessage(MISSING_PLACEHOLDER_KEY));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ca.gc.aafc.dina.vocabulary;

import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;

/**
* Utility class to have uniform handling of key generation from a name.
*/
public final class VocabularyKeyHelper {

private static final Pattern NON_ALPHANUMERICAL = Pattern.compile("[^a-z0-9]");

private VocabularyKeyHelper() {
// utility class
}

/**
* Transforms a name into a key. camelCase is supported.
* "Aa bb !! mySuperAttribute # 11" will become aa_bb_my_super_attribute_11
* @param name
* @return
*/
public static String generateKeyFromName(String name) {
Objects.requireNonNull(name);

return Arrays.stream(StringUtils.
splitByCharacterTypeCamelCase(StringUtils.normalizeSpace(name)))
.filter(StringUtils::isNotBlank)
.map(VocabularyKeyHelper::processName)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining("_"));
}

private static String processName(String name) {
return RegExUtils.removeAll(name.toLowerCase(), NON_ALPHANUMERICAL);
}

public static boolean isKeyValid(String key) {
if (StringUtils.isBlank(key)) {
return false;
}
// use the key as the name and check if we get the same result
return key.equals(generateKeyFromName(key));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ location.nullWellCol=Well column cannot be null when well row is set.
location.nullWellRow=Well row cannot be null when well column is set.
location.invalidWellColumn=Invalid well column: {0}
location.invalidWellRow=Invalid well row: {0}

identifierType.uriTemplate.missingPlaceholder=Missing identifier placeholder $1
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ca.gc.aafc.dina.service;

import org.hibernate.annotations.Type;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.validation.SmartValidator;

import ca.gc.aafc.dina.TestDinaBaseApp;
import ca.gc.aafc.dina.entity.IdentifierType;
import ca.gc.aafc.dina.i18n.MultilingualTitle;
import ca.gc.aafc.dina.jpa.BaseDAO;
import ca.gc.aafc.dina.testsupport.PostgresTestContainerInitializer;
import ca.gc.aafc.dina.validation.IdentifierTypeValidator;

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

import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.transaction.Transactional;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

@SpringBootTest(classes = {TestDinaBaseApp.class, IdentifierTypeServiceIT.IdentifierTypeServiceConfig.class})
@ContextConfiguration(initializers = { PostgresTestContainerInitializer.class })
public class IdentifierTypeServiceIT {

@Inject
private IdentifierTypeService<IdentifierType> identifierTypeService;

@Test
@Transactional
public void identifierTypeService_OnCreate_KeyCorrectlyGenerated() {

IdentifierType identifierType = identifierTypeService
.create(TestIdentifierType.builder()
.id(11)
.uuid(UUID.randomUUID())
.uriTemplate("https://abc.abc/$1")
.dinaComponents(List.of("project", "person"))
.name("dina specialIdentifier #11").build());

assertEquals("dina_special_identifier_11", identifierType.getKey());
}

@TestConfiguration
@EntityScan(basePackageClasses = TestIdentifierType.class)
static class IdentifierTypeServiceConfig {

@Bean
public IdentifierTypeService<IdentifierType> identifierTypeService(BaseDAO baseDAO,
SmartValidator sv,
IdentifierTypeValidator iv) {
return new IdentifierTypeService<>(baseDAO, sv, iv) {
};
}

@Bean
public IdentifierTypeValidator identifierTypeValidator(@Named("validationMessageSource")
MessageSource messageSource) {
return new IdentifierTypeValidator(messageSource);
}
}

@Getter
@Setter
@SuperBuilder
@Entity
static class TestIdentifierType implements IdentifierType {

@Id
private Integer id;
private UUID uuid;
private String key;

private String name;
private String term;

@Type(type = "jsonb")
private MultilingualTitle multilingualTitle;

@Type(type = "list-array")
private List<String> dinaComponents;

private String uriTemplate;
private String createdBy;
private OffsetDateTime createdOn;
}

}

0 comments on commit 1ff548c

Please sign in to comment.