-
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
- Loading branch information
1 parent
1eb09ea
commit f543e6f
Showing
22 changed files
with
660 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
8 changes: 8 additions & 0 deletions
8
src/main/java/org/folio/linked/data/exception/SearchException.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,8 @@ | ||
package org.folio.linked.data.exception; | ||
|
||
public class SearchException extends RuntimeException { | ||
|
||
public SearchException(String message, Throwable cause) { | ||
super(message, cause); | ||
} | ||
} |
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")); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
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,25 @@ | ||
package org.folio.linked.data.validation; | ||
|
||
import jakarta.validation.Constraint; | ||
import jakarta.validation.Payload; | ||
import jakarta.validation.Valid; | ||
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 | ||
@Constraint(validatedBy = LccnUniquenessValidator.class) | ||
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Valid | ||
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.