From 10eebefd85184c621b68c7e7d2d7ded382c7920f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Parko=C5=82a?= <605123+tparkola@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:32:21 +0200 Subject: [PATCH 1/2] [SSHOC-423] Proper encoding of URIs in concepts. Removed utilityclass (experimental lombok feature) from MatcherUtils. --- .../vocabularies/ConceptFactory.java | 34 +++++++++++++++---- .../vocabularies/ConceptControllerITCase.java | 4 +-- .../marketplace/util/MatcherUtils.java | 3 +- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java b/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java index be551988..964f2a87 100644 --- a/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java +++ b/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java @@ -7,12 +7,18 @@ import eu.sshopencloud.marketplace.repositories.vocabularies.ConceptRepository; import eu.sshopencloud.marketplace.validators.ValidationException; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.Errors; +import org.springframework.web.util.UriUtils; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -22,6 +28,7 @@ @Service @Transactional @RequiredArgsConstructor +@Slf4j public class ConceptFactory { private final ConceptRepository conceptRepository; @@ -62,14 +69,13 @@ public Concept create(ConceptCore conceptCore, Vocabulary vocabulary, String cod concept.setDefinition(conceptCore.getDefinition()); - if (StringUtils.isBlank(conceptCore.getUri())) { - concept.setUri(vocabulary.getNamespace() + concept.getCode()); + if (StringUtils.isNotBlank(conceptCore.getUri()) && + !StringUtils.equals(conceptCore.getUri(), vocabulary.getNamespace() + concept.getCode()) && + !StringUtils.equals(conceptCore.getUri(), vocabulary.getNamespace() + URLEncoder.encode(concept.getCode(), StandardCharsets.UTF_8))) { + errors.rejectValue("uri", "field.invalid", + "Uri is not consistent with vocabulary namespace and concept code."); } else { - if (conceptCore.getUri().equals(vocabulary.getNamespace() + concept.getCode())) { - concept.setUri(conceptCore.getUri()); - } else { - errors.rejectValue("uri", "field.invalid", "Uri is not consistent with vocabulary namespace and concept code."); - } + concept.setUri(vocabulary.getNamespace() + (isURLEncoded(concept.getCode()) ? concept.getCode() : UriUtils.encode(concept.getCode(), StandardCharsets.UTF_8))); } if (errors.hasErrors()) @@ -78,6 +84,20 @@ public Concept create(ConceptCore conceptCore, Vocabulary vocabulary, String cod return concept; } + private boolean isURLEncoded(String code) { + try { + String decoded = UriUtils.decode(code, StandardCharsets.UTF_8); + String reEncoded = UriUtils.encode(decoded, StandardCharsets.UTF_8); + if (code.equals(reEncoded)) { + return true; + } + } catch (IllegalArgumentException e) { + log.debug(e.getMessage()); + return false; + } + return false; + } + public List createConceptRelations(Concept concept, List relatedConcepts) throws ValidationException { BeanPropertyBindingResult errors = new BeanPropertyBindingResult(relatedConcepts, "Concept"); diff --git a/src/test/java/eu/sshopencloud/marketplace/controllers/vocabularies/ConceptControllerITCase.java b/src/test/java/eu/sshopencloud/marketplace/controllers/vocabularies/ConceptControllerITCase.java index 1f0cc97c..6d66a85c 100644 --- a/src/test/java/eu/sshopencloud/marketplace/controllers/vocabularies/ConceptControllerITCase.java +++ b/src/test/java/eu/sshopencloud/marketplace/controllers/vocabularies/ConceptControllerITCase.java @@ -127,7 +127,7 @@ public void shouldCreateNewCandidateConcept() throws Exception { .andExpect(jsonPath("$.code", is("New Candidate"))) .andExpect(jsonPath("$.label", is("New candidate concept"))) .andExpect(jsonPath("$.candidate", is(true))) - .andExpect(jsonPath("$.uri", is("http://purl.org/ontology/bibo/New Candidate"))) + .andExpect(jsonPath("$.uri", is("http://purl.org/ontology/bibo/New%20Candidate"))) .andExpect(jsonPath("$.relatedConcepts[0].code", is("Journal"))) .andExpect(jsonPath("$.relatedConcepts[1].code", is("Book"))) .andExpect(jsonPath("$.relatedConcepts[2].code", is("Conference"))); @@ -447,7 +447,7 @@ public void shouldDeleteAndCreateConcept() throws Exception { .andExpect(jsonPath("$.code", is("New Candidate"))) .andExpect(jsonPath("$.label", is("New candidate concept"))) .andExpect(jsonPath("$.candidate", is(true))) - .andExpect(jsonPath("$.uri", is("http://purl.org/ontology/bibo/New Candidate"))) + .andExpect(jsonPath("$.uri", is("http://purl.org/ontology/bibo/New%20Candidate"))) .andExpect(jsonPath("$.relatedConcepts[0].code", is("Journal"))) .andExpect(jsonPath("$.relatedConcepts[1].code", is("Conference"))); diff --git a/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java b/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java index 1bcd2938..71bf2574 100644 --- a/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java +++ b/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java @@ -6,10 +6,9 @@ import org.hamcrest.TypeSafeMatcher; -@UtilityClass public class MatcherUtils { - public Matcher equalValue(long value) { + public static Matcher equalValue(long value) { return new TypeSafeMatcher<>() { @Override protected boolean matchesSafely(Number item) { From 6a754d72bb8146f76ecc94daae3a5d9357c6feb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Parko=C5=82a?= <605123+tparkola@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:34:07 +0200 Subject: [PATCH 2/2] [SSHOC-423] Cleanup of unused imports. --- .../validators/vocabularies/ConceptFactory.java | 8 ++++---- .../eu/sshopencloud/marketplace/util/MatcherUtils.java | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java b/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java index 964f2a87..12169f59 100644 --- a/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java +++ b/src/main/java/eu/sshopencloud/marketplace/validators/vocabularies/ConceptFactory.java @@ -3,11 +3,13 @@ import eu.sshopencloud.marketplace.dto.vocabularies.ConceptCore; import eu.sshopencloud.marketplace.dto.vocabularies.RelatedConceptCore; import eu.sshopencloud.marketplace.dto.vocabularies.VocabularyId; -import eu.sshopencloud.marketplace.model.vocabularies.*; +import eu.sshopencloud.marketplace.model.vocabularies.Concept; +import eu.sshopencloud.marketplace.model.vocabularies.ConceptId; +import eu.sshopencloud.marketplace.model.vocabularies.ConceptRelatedConcept; +import eu.sshopencloud.marketplace.model.vocabularies.Vocabulary; import eu.sshopencloud.marketplace.repositories.vocabularies.ConceptRepository; import eu.sshopencloud.marketplace.validators.ValidationException; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -16,12 +18,10 @@ import org.springframework.validation.Errors; import org.springframework.web.util.UriUtils; -import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Optional; diff --git a/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java b/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java index 71bf2574..a4532dfc 100644 --- a/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java +++ b/src/test/java/eu/sshopencloud/marketplace/util/MatcherUtils.java @@ -1,6 +1,5 @@ package eu.sshopencloud.marketplace.util; -import lombok.experimental.UtilityClass; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher;