-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MODLD-582: LCCN Validation | Ensure uniquely assigned
MODLD-582: Change lccn value in test MODLD-582: Additional verification for searchClient invocation MODLD-582: Review/Sonar fixes MODLD-582: Review/Sonar fixes MODLD-582: Handle failed dependency MODLD-582: Review/Sonar fixes MODLD-582: Review/Sonar fixes MODLD-582: Rework exception handling in search service MODLD-582 Review fixes MODLD-582 Review fixes MODLD-582: Fix sonar/review MODLD-582: Fix sonar/review
- Loading branch information
1 parent
2fbcc74
commit 6c79fbc
Showing
22 changed files
with
652 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
src/main/java/org/folio/linked/data/client/SearchClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.folio.linked.data.client; | ||
|
||
import org.folio.linked.data.domain.dto.SearchResponseTotalOnly; | ||
import org.springframework.cloud.openfeign.FeignClient; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
|
||
@FeignClient(name = "search") | ||
public interface SearchClient { | ||
|
||
@GetMapping("/instances") | ||
ResponseEntity<SearchResponseTotalOnly> searchInstances(@RequestParam("query") String query); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
src/main/java/org/folio/linked/data/service/search/InstanceSearchService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.folio.linked.data.service.search; | ||
|
||
import java.util.Collection; | ||
import org.folio.linked.data.domain.dto.SearchResponseTotalOnly; | ||
|
||
public interface InstanceSearchService { | ||
|
||
SearchResponseTotalOnly searchByLccn(Collection<String> lccn); | ||
|
||
SearchResponseTotalOnly searchByLccnExcludingId(Collection<String> lccn, String instanceId); | ||
} |
48 changes: 48 additions & 0 deletions
48
src/main/java/org/folio/linked/data/service/search/InstanceSearchServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.folio.linked.data.service.search; | ||
|
||
import static java.lang.String.format; | ||
import static java.util.Objects.isNull; | ||
import static java.util.stream.Collectors.joining; | ||
|
||
import java.util.Collection; | ||
import java.util.Objects; | ||
import lombok.RequiredArgsConstructor; | ||
import org.folio.linked.data.client.SearchClient; | ||
import org.folio.linked.data.domain.dto.SearchResponseTotalOnly; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class InstanceSearchServiceImpl implements InstanceSearchService { | ||
|
||
private static final String EXCLUDE_SUPPRESSED = "staffSuppress <> \"true\" and discoverySuppress <> \"true\""; | ||
private static final String ID_NOT_EQUALS = "id <> \"%s\""; | ||
private static final String LCCN_EQUALS = "lccn==\"%s\""; | ||
|
||
private final SearchClient searchClient; | ||
|
||
@Override | ||
public SearchResponseTotalOnly searchByLccn(Collection<String> lccn) { | ||
return search(getLccnQuery(lccn)); | ||
} | ||
|
||
@Override | ||
public SearchResponseTotalOnly searchByLccnExcludingId(Collection<String> lccn, String instanceId) { | ||
if (isNull(instanceId)) { | ||
return searchByLccn(lccn); | ||
} | ||
return search(format("%s and %s", getLccnQuery(lccn), format(ID_NOT_EQUALS, instanceId))); | ||
} | ||
|
||
private SearchResponseTotalOnly search(String query) { | ||
return searchClient.searchInstances(query).getBody(); | ||
} | ||
|
||
private String getLccnQuery(Collection<String> lccnCol) { | ||
var orLccnQuery = lccnCol.stream() | ||
.filter(Objects::nonNull) | ||
.map(lccn -> format(LCCN_EQUALS, lccn)) | ||
.collect(joining(" or ")); | ||
return format("(%s) and (%s)", orLccnQuery, EXCLUDE_SUPPRESSED); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.folio.linked.data.util; | ||
|
||
import static org.apache.commons.collections4.CollectionUtils.isEmpty; | ||
|
||
import lombok.experimental.UtilityClass; | ||
import org.folio.linked.data.domain.dto.LccnRequest; | ||
|
||
@UtilityClass | ||
public class LccnUtils { | ||
|
||
public static boolean isCurrent(LccnRequest lccnRequest) { | ||
return isEmpty(lccnRequest.getStatus()) || lccnRequest.getStatus() | ||
.stream() | ||
.flatMap(status -> status.getLink().stream()) | ||
.anyMatch(link -> link.endsWith("current")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/main/java/org/folio/linked/data/validation/LccnUniqueConstraint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.folio.linked.data.validation; | ||
|
||
import jakarta.validation.Constraint; | ||
import jakarta.validation.Payload; | ||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import org.folio.linked.data.validation.dto.LccnUniquenessValidator; | ||
|
||
@Documented | ||
@SuppressWarnings("javaarchitecture:S7091") | ||
@Constraint(validatedBy = LccnUniquenessValidator.class) | ||
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface LccnUniqueConstraint { | ||
|
||
String message() default "{lccnUniqueConstraint.message}"; | ||
|
||
Class<?>[] groups() default {}; | ||
|
||
Class<? extends Payload>[] payload() default {}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
src/main/java/org/folio/linked/data/validation/dto/LccnUniquenessValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package org.folio.linked.data.validation.dto; | ||
|
||
import static java.util.Optional.ofNullable; | ||
|
||
import jakarta.validation.ConstraintValidator; | ||
import jakarta.validation.ConstraintValidatorContext; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.stream.Stream; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.log4j.Log4j2; | ||
import org.folio.linked.data.domain.dto.InstanceField; | ||
import org.folio.linked.data.domain.dto.LccnField; | ||
import org.folio.linked.data.domain.dto.LccnRequest; | ||
import org.folio.linked.data.domain.dto.ResourceRequestDto; | ||
import org.folio.linked.data.domain.dto.SearchResponseTotalOnly; | ||
import org.folio.linked.data.exception.RequestProcessingExceptionBuilder; | ||
import org.folio.linked.data.repo.FolioMetadataRepository; | ||
import org.folio.linked.data.service.search.InstanceSearchService; | ||
import org.folio.linked.data.util.LccnUtils; | ||
import org.folio.linked.data.validation.LccnUniqueConstraint; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
@Log4j2 | ||
public class LccnUniquenessValidator implements ConstraintValidator<LccnUniqueConstraint, ResourceRequestDto> { | ||
|
||
private final InstanceSearchService instanceSearchService; | ||
private final FolioMetadataRepository folioMetadataRepository; | ||
private final RequestProcessingExceptionBuilder exceptionBuilder; | ||
|
||
@Override | ||
public boolean isValid(ResourceRequestDto resourceRequestDto, ConstraintValidatorContext constraintValidatorContext) { | ||
var resource = resourceRequestDto.getResource(); | ||
if (resource instanceof InstanceField instance && hasCurrentLccn(instance)) { | ||
var inventoryId = findInventoryId(resourceRequestDto); | ||
return isUnique(getLccnValues(instance), inventoryId); | ||
} | ||
return true; | ||
} | ||
|
||
private String findInventoryId(ResourceRequestDto resourceRequestDto) { | ||
return ofNullable(resourceRequestDto.getId()) | ||
.flatMap(folioMetadataRepository::findInventoryIdById) | ||
.map(FolioMetadataRepository.InventoryIdOnly::getInventoryId) | ||
.orElse(null); | ||
} | ||
|
||
private boolean isUnique(List<String> lccn, String inventoryId) { | ||
return ofNullable(findInstanceWithLccn(lccn, inventoryId)) | ||
.map(SearchResponseTotalOnly::getTotalRecords) | ||
.map(count -> count == 0) | ||
.orElse(false); | ||
} | ||
|
||
private SearchResponseTotalOnly findInstanceWithLccn(List<String> lccn, String inventoryId) { | ||
try { | ||
return instanceSearchService.searchByLccnExcludingId(lccn, inventoryId); | ||
} catch (Exception e) { | ||
log.error(e); | ||
throw exceptionBuilder.failedDependencyException( | ||
"Could not validate LCCN for duplicate", "Unable to reach search service"); | ||
} | ||
} | ||
|
||
private boolean hasCurrentLccn(InstanceField instance) { | ||
return getLccnRequest(instance).anyMatch(LccnUtils::isCurrent); | ||
} | ||
|
||
private List<String> getLccnValues(InstanceField instance) { | ||
return getLccnRequest(instance) | ||
.filter(LccnUtils::isCurrent) | ||
.map(LccnRequest::getValue) | ||
.flatMap(Collection::stream) | ||
.toList(); | ||
} | ||
|
||
private Stream<LccnRequest> getLccnRequest(InstanceField instance) { | ||
return instance.getInstance() | ||
.getMap() | ||
.stream() | ||
.filter(LccnField.class::isInstance) | ||
.map(i -> (LccnField) i) | ||
.map(LccnField::getLccn); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
primaryTitleConstraint.message=required_primary_main_title | ||
lccnPatternConstraint.message=lccn_does_not_match_pattern | ||
lccnUniqueConstraint.message=lccn_not_unique |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.