From 664efc34473fcd1b454069862f8a7607e2edf3a5 Mon Sep 17 00:00:00 2001 From: askhat-abishev Date: Thu, 21 Nov 2024 16:59:22 +0500 Subject: [PATCH 1/3] MODLD-599: LCCN validation configuration --- descriptors/ModuleDescriptor-template.json | 12 +- pom.xml | 6 + .../linked/data/LinkedDataApplication.java | 2 + .../folio/linked/data/client/SpecClient.java | 19 ++++ .../configuration/CacheConfiguration.java | 21 ++++ .../validation/dto/LccnPatternValidator.java | 16 ++- .../data/validation/spec/SpecProvider.java | 9 ++ .../spec/impl/SpecProviderImpl.java | 58 ++++++++++ src/main/resources/application.yml | 3 + .../resource/ResourceControllerITBase.java | 19 ++++ .../dto/LccnPatternValidatorTest.java | 103 +++++++++++------- .../spec/impl/SpecProviderImplTest.java | 58 ++++++++++ 12 files changed, 284 insertions(+), 42 deletions(-) create mode 100644 src/main/java/org/folio/linked/data/client/SpecClient.java create mode 100644 src/main/java/org/folio/linked/data/configuration/CacheConfiguration.java create mode 100644 src/main/java/org/folio/linked/data/validation/spec/SpecProvider.java create mode 100644 src/main/java/org/folio/linked/data/validation/spec/impl/SpecProviderImpl.java create mode 100644 src/test/java/org/folio/linked/data/validation/spec/impl/SpecProviderImplTest.java diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 283465bf..fef7629e 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -25,7 +25,9 @@ "source-storage.snapshots.post", "source-storage.records.formatted.item.get", "source-storage.records.post", - "source-storage.records.put" + "source-storage.records.put", + "specification-storage.specifications.collection.get", + "specification-storage.specification.rules.collection.get" ] }, { @@ -65,7 +67,9 @@ "source-storage.snapshots.post", "source-storage.records.formatted.item.get", "source-storage.records.post", - "source-storage.records.put" + "source-storage.records.put", + "specification-storage.specifications.collection.get", + "specification-storage.specification.rules.collection.get" ] }, { @@ -172,6 +176,10 @@ { "id": "source-storage-records", "version": "3.2 3.3" + }, + { + "id": "specification-storage", + "version": "1.0" } ], "permissionSets": [ diff --git a/pom.xml b/pom.xml index 0061791b..9bac90be 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ 1.0.2-SNAPSHOT 1.0.1-SNAPSHOT 5.9.1 + 1.1.0-SNAPSHOT 10.20.1 @@ -102,6 +103,11 @@ mod-source-record-storage-client ${mod-source-record-storage-client.version} + + org.folio + mod-record-specifications-dto + ${mod-record-specifications-dto.version} + org.folio folio-kafka-wrapper diff --git a/src/main/java/org/folio/linked/data/LinkedDataApplication.java b/src/main/java/org/folio/linked/data/LinkedDataApplication.java index dc06287a..584494a2 100644 --- a/src/main/java/org/folio/linked/data/LinkedDataApplication.java +++ b/src/main/java/org/folio/linked/data/LinkedDataApplication.java @@ -9,10 +9,12 @@ import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; @EnableCaching @EnableAsync @EnableFeignClients +@EnableScheduling @SpringBootApplication @ComponentScan(value = "org.folio", excludeFilters = @Filter(type = REGEX, pattern = {"org.folio.spring.tools.systemuser.*", "org.folio.spring.tools.batch.*"})) diff --git a/src/main/java/org/folio/linked/data/client/SpecClient.java b/src/main/java/org/folio/linked/data/client/SpecClient.java new file mode 100644 index 00000000..94ab56f0 --- /dev/null +++ b/src/main/java/org/folio/linked/data/client/SpecClient.java @@ -0,0 +1,19 @@ +package org.folio.linked.data.client; + +import java.util.UUID; +import org.folio.rspec.domain.dto.SpecificationDtoCollection; +import org.folio.rspec.domain.dto.SpecificationRuleDtoCollection; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@FeignClient(name = "specification-storage") +public interface SpecClient { + + @GetMapping(value = "/specifications?profile=bibliographic&family=MARC") + ResponseEntity getBibMarcSpecs(); + + @GetMapping(value = "/specifications/{specId}/rules") + ResponseEntity getSpecRules(@PathVariable("specId") UUID specId); +} diff --git a/src/main/java/org/folio/linked/data/configuration/CacheConfiguration.java b/src/main/java/org/folio/linked/data/configuration/CacheConfiguration.java new file mode 100644 index 00000000..af0049a8 --- /dev/null +++ b/src/main/java/org/folio/linked/data/configuration/CacheConfiguration.java @@ -0,0 +1,21 @@ +package org.folio.linked.data.configuration; + +import java.util.Collections; +import org.springframework.cache.CacheManager; +import org.springframework.cache.concurrent.ConcurrentMapCache; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CacheConfiguration { + + public static final String SPEC_RULES = "specRules"; + + @Bean + public CacheManager cacheManager() { + var cacheManager = new SimpleCacheManager(); + cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache(SPEC_RULES))); + return cacheManager; + } +} diff --git a/src/main/java/org/folio/linked/data/validation/dto/LccnPatternValidator.java b/src/main/java/org/folio/linked/data/validation/dto/LccnPatternValidator.java index dcb87fc8..961fd4cd 100644 --- a/src/main/java/org/folio/linked/data/validation/dto/LccnPatternValidator.java +++ b/src/main/java/org/folio/linked/data/validation/dto/LccnPatternValidator.java @@ -1,21 +1,35 @@ package org.folio.linked.data.validation.dto; +import static java.lang.Boolean.TRUE; import static org.apache.commons.collections4.CollectionUtils.isEmpty; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import java.util.regex.Pattern; +import lombok.RequiredArgsConstructor; import org.folio.linked.data.domain.dto.LccnRequest; import org.folio.linked.data.validation.LccnPatternConstraint; +import org.folio.linked.data.validation.spec.SpecProvider; +import org.folio.rspec.domain.dto.SpecificationRuleDto; +@RequiredArgsConstructor public class LccnPatternValidator implements ConstraintValidator { private static final Pattern LCCN_STRUCTURE_A_PATTERN = Pattern.compile("( {3}|[a-z][ |a-z]{2})\\d{8} "); private static final Pattern LCCN_STRUCTURE_B_PATTERN = Pattern.compile("( {2}|[a-z][ |a-z])\\d{10}"); + private final SpecProvider specProvider; + @Override public boolean isValid(LccnRequest lccnRequest, ConstraintValidatorContext constraintValidatorContext) { - if (isCurrent(lccnRequest)) { + var isEnabled = specProvider.getSpecRules() + .stream() + .filter(rule -> rule.getCode().equals("invalidLccnSubfieldValue")) + .findFirst() + .map(SpecificationRuleDto::getEnabled) + .orElse(false); + + if (TRUE.equals(isEnabled) && isCurrent(lccnRequest)) { return lccnRequest.getValue() .stream() .anyMatch(this::hasValidPattern); diff --git a/src/main/java/org/folio/linked/data/validation/spec/SpecProvider.java b/src/main/java/org/folio/linked/data/validation/spec/SpecProvider.java new file mode 100644 index 00000000..04c101d3 --- /dev/null +++ b/src/main/java/org/folio/linked/data/validation/spec/SpecProvider.java @@ -0,0 +1,9 @@ +package org.folio.linked.data.validation.spec; + +import java.util.List; +import org.folio.rspec.domain.dto.SpecificationRuleDto; + +public interface SpecProvider { + + List getSpecRules(); +} diff --git a/src/main/java/org/folio/linked/data/validation/spec/impl/SpecProviderImpl.java b/src/main/java/org/folio/linked/data/validation/spec/impl/SpecProviderImpl.java new file mode 100644 index 00000000..a47d01ec --- /dev/null +++ b/src/main/java/org/folio/linked/data/validation/spec/impl/SpecProviderImpl.java @@ -0,0 +1,58 @@ +package org.folio.linked.data.validation.spec.impl; + +import static org.folio.linked.data.configuration.CacheConfiguration.SPEC_RULES; + +import feign.FeignException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.folio.linked.data.client.SpecClient; +import org.folio.linked.data.validation.spec.SpecProvider; +import org.folio.rspec.domain.dto.SpecificationDto; +import org.folio.rspec.domain.dto.SpecificationDtoCollection; +import org.folio.rspec.domain.dto.SpecificationRuleDto; +import org.folio.rspec.domain.dto.SpecificationRuleDtoCollection; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SpecProviderImpl implements SpecProvider { + + private final SpecClient client; + + @Cacheable(value = SPEC_RULES) + @Override + public List getSpecRules() { + try { + return Optional.ofNullable(client.getBibMarcSpecs().getBody()) + .map(SpecificationDtoCollection::getSpecifications) + .stream() + .flatMap(Collection::stream) + .findFirst() + .map(SpecificationDto::getId) + .map(client::getSpecRules) + .map(ResponseEntity::getBody) + .map(SpecificationRuleDtoCollection::getRules) + .stream() + .flatMap(Collection::stream) + .toList(); + } catch (FeignException e) { + log.error("Unexpected exception during specification rules retrieval", e); + return Collections.emptyList(); + } + } + + @CacheEvict(value = SPEC_RULES) + @Scheduled(fixedRateString = "${mod-linked-data.caching.ttl.specRules}") + public void emptyCache() { + log.info("Emptying {} cache", SPEC_RULES); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 15a706e0..a643b55f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -79,6 +79,9 @@ mod-linked-data: topic: work-search-index: ${KAFKA_WORK_SEARCH_INDEX_TOPIC:linked-data.work} instance-ingress: ${KAFKA_INVENTORY_INSTANCE_INGRESS_EVENT_TOPIC:inventory.instance_ingress} + caching: + ttl: + specRules: 18000000 management: endpoints: diff --git a/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerITBase.java b/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerITBase.java index 9bed359a..1cfe6def 100644 --- a/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerITBase.java +++ b/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerITBase.java @@ -4,6 +4,7 @@ import static java.lang.String.join; import static java.util.Spliterator.ORDERED; import static java.util.Spliterators.spliteratorUnknownSize; +import static java.util.UUID.randomUUID; import static java.util.stream.StreamSupport.stream; import static org.assertj.core.api.Assertions.assertThat; import static org.folio.ld.dictionary.PredicateDictionary.ACCESS_LOCATION; @@ -154,6 +155,7 @@ import org.folio.ld.dictionary.PredicateDictionary; import org.folio.ld.dictionary.PropertyDictionary; import org.folio.ld.dictionary.ResourceTypeDictionary; +import org.folio.linked.data.client.SpecClient; import org.folio.linked.data.client.SrsClient; import org.folio.linked.data.domain.dto.InstanceResponseField; import org.folio.linked.data.domain.dto.ResourceIndexEventType; @@ -169,6 +171,10 @@ import org.folio.linked.data.test.TestUtil; import org.folio.rest.jaxrs.model.ParsedRecord; import org.folio.rest.jaxrs.model.Record; +import org.folio.rspec.domain.dto.SpecificationDto; +import org.folio.rspec.domain.dto.SpecificationDtoCollection; +import org.folio.rspec.domain.dto.SpecificationRuleDto; +import org.folio.rspec.domain.dto.SpecificationRuleDtoCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -214,6 +220,8 @@ public abstract class ResourceControllerITBase { private HashService hashService; @MockBean private SrsClient srsClient; + @MockBean + private SpecClient specClient; @BeforeEach public void beforeEach() { @@ -376,6 +384,17 @@ void createWorkWithInstanceRef_shouldSaveEntityCorrectly() throws Exception { @Test void update_shouldReturnCorrectlyUpdatedInstanceWithWorkRef_deleteOldOne_sendMessages() throws Exception { // given + var specifications = new SpecificationDtoCollection(); + var specRuleId = randomUUID(); + specifications.setSpecifications(List.of(new SpecificationDto().id(specRuleId))); + var specRules = new SpecificationRuleDtoCollection(); + var specRule = new SpecificationRuleDto(); + specRule.setCode("invalidLccnSubfieldValue"); + specRule.setEnabled(true); + specRules.setRules(List.of(specRule)); + when(specClient.getBibMarcSpecs()).thenReturn(ResponseEntity.ok().body(specifications)); + when(specClient.getSpecRules(specRuleId)).thenReturn(ResponseEntity.ok().body(specRules)); + var work = getSampleWork(null); var originalInstance = resourceTestService.saveGraph(getSampleInstanceResource(null, work)); var updateDto = getSampleInstanceDtoMap(); diff --git a/src/test/java/org/folio/linked/data/validation/dto/LccnPatternValidatorTest.java b/src/test/java/org/folio/linked/data/validation/dto/LccnPatternValidatorTest.java index 66f03b6e..d1f78033 100644 --- a/src/test/java/org/folio/linked/data/validation/dto/LccnPatternValidatorTest.java +++ b/src/test/java/org/folio/linked/data/validation/dto/LccnPatternValidatorTest.java @@ -4,85 +4,103 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.mockito.Mockito.doReturn; import java.util.List; import java.util.stream.Stream; import org.folio.linked.data.domain.dto.LccnRequest; import org.folio.linked.data.domain.dto.Status; +import org.folio.linked.data.validation.spec.SpecProvider; +import org.folio.rspec.domain.dto.SpecificationRuleDto; import org.folio.spring.testing.type.UnitTest; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; @UnitTest +@ExtendWith(MockitoExtension.class) class LccnPatternValidatorTest { - private final LccnPatternValidator validator = new LccnPatternValidator(); + @Mock + private SpecProvider specProvider; + + @InjectMocks + private LccnPatternValidator validator; @ParameterizedTest - @MethodSource("validLccnProvider") - void shouldReturnTrue_ifLccnIsValid(String value, String link) { + @MethodSource("positiveCaseProvider") + void shouldReturnTrue_ifLccnIsValid(String value, String link, List specRules) { // given var lccnRequest = createLccnRequest(value, link); + doReturn(specRules).when(specProvider).getSpecRules(); // expect assertTrue(validator.isValid(lccnRequest, null)); } - private static Stream validLccnProvider() { + private static Stream positiveCaseProvider() { return Stream.of( // structure A - arguments(" 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("n 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nn 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nnn12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nnn12345678 ", null), - arguments(" nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/cancinv"), + arguments(" 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("n 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nn 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nnn12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nnn12345678 ", null, List.of(createSpecRule(true))), + arguments(" nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/cancinv", List.of(createSpecRule(true))), + arguments(" nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(false))), + arguments(" nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of()), // structure B - arguments(" 0123456789", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("n 0123456781", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nn0123456789", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nn0123456789", null), - arguments("mmm0123456789", "http://id.loc.gov/vocabulary/mstatus/cancinv")); + arguments(" 0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("n 0123456781", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nn0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nn0123456789", null, List.of(createSpecRule(true))), + arguments("mmm0123456789", "http://id.loc.gov/vocabulary/mstatus/cancinv", List.of(createSpecRule(true))), + arguments("mmm0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(false))), + arguments("mmm0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of())); } @ParameterizedTest - @MethodSource("invalidLccnProvider") - void shouldReturnFalse_ifLccnIsInvalid(String value, String link) { + @MethodSource("negativeCaseProvider") + void shouldReturnFalse_ifLccnIsInvalid(String value, String link, List specRules) { // given var lccnRequest = createLccnRequest(value, link); + doReturn(specRules).when(specProvider).getSpecRules(); // expect assertFalse(validator.isValid(lccnRequest, null)); } - public static Stream invalidLccnProvider() { + public static Stream negativeCaseProvider() { return Stream.of( // structure A - arguments(" 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" n 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" n12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" 1234567 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" 12345678", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nnnn12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nNn12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nNn12345678 ", null), - arguments("n-n12345678 ", "http://id.loc.gov/vocabulary/mstatus/current"), + arguments(" 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" n 12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" n12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" 1234567 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" 12345678", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nnn123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nnnn12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nNn12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nNn12345678 ", null, List.of(createSpecRule(true))), + arguments("n-n12345678 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), // structure B - arguments(" 0123456789", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" m123456789", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" m123456789 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments(" mm123456789 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("mm123456789 ", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("mmm0123456789", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nN0123456789", "http://id.loc.gov/vocabulary/mstatus/current"), - arguments("nN0123456789", null), - arguments("n-0123456789", "http://id.loc.gov/vocabulary/mstatus/current")); + arguments(" 0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" m123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" m123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments(" mm123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("mm123456789 ", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("mmm0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nN0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true))), + arguments("nN0123456789", null, List.of(createSpecRule(true))), + arguments("n-0123456789", "http://id.loc.gov/vocabulary/mstatus/current", List.of(createSpecRule(true)))); } private LccnRequest createLccnRequest(String value, String link) { @@ -90,4 +108,11 @@ private LccnRequest createLccnRequest(String value, String link) { .value(List.of(value)) .status(link == null ? emptyList() : List.of(new Status().link(List.of(link)))); } + + private static SpecificationRuleDto createSpecRule(boolean enabled) { + var specRule = new SpecificationRuleDto(); + specRule.setCode("invalidLccnSubfieldValue"); + specRule.setEnabled(enabled); + return specRule; + } } diff --git a/src/test/java/org/folio/linked/data/validation/spec/impl/SpecProviderImplTest.java b/src/test/java/org/folio/linked/data/validation/spec/impl/SpecProviderImplTest.java new file mode 100644 index 00000000..72140a69 --- /dev/null +++ b/src/test/java/org/folio/linked/data/validation/spec/impl/SpecProviderImplTest.java @@ -0,0 +1,58 @@ +package org.folio.linked.data.validation.spec.impl; + +import static java.util.Collections.emptyList; +import static java.util.UUID.randomUUID; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +import feign.FeignException; +import java.util.List; +import org.folio.linked.data.client.SpecClient; +import org.folio.rspec.domain.dto.SpecificationDto; +import org.folio.rspec.domain.dto.SpecificationDtoCollection; +import org.folio.rspec.domain.dto.SpecificationRuleDto; +import org.folio.rspec.domain.dto.SpecificationRuleDtoCollection; +import org.folio.spring.testing.type.UnitTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; + +@UnitTest +@ExtendWith(MockitoExtension.class) +class SpecProviderImplTest { + + @Mock + private SpecClient client; + + @InjectMocks + private SpecProviderImpl specProvider; + + @Test + void shouldReturn_specRules_whenSpecificationStorageIsAvailable() { + //given + var specRuleId = randomUUID(); + var specifications = new SpecificationDtoCollection(); + var specRule = new SpecificationRuleDto(); + var specRules = new SpecificationRuleDtoCollection(); + specRules.setRules(List.of(specRule)); + specifications.setSpecifications(List.of(new SpecificationDto().id(specRuleId))); + doReturn(ResponseEntity.ok().body(specifications)).when(client).getBibMarcSpecs(); + doReturn(ResponseEntity.ok().body(specRules)).when(client).getSpecRules(specRuleId); + + //expect + assertEquals(List.of(specRule), specProvider.getSpecRules()); + } + + @Test + void shouldReturn_emptyList_whenSpecificationStorageIsNotAvailable() { + //given + when(client.getBibMarcSpecs()).thenThrow(FeignException.class); + + //expect + assertEquals(emptyList(), specProvider.getSpecRules()); + } +} From b5d336bca3a3423fe5c3e6d00f7b7098df90829b Mon Sep 17 00:00:00 2001 From: askhat-abishev Date: Fri, 22 Nov 2024 16:35:03 +0500 Subject: [PATCH 2/3] MODLD-599: fix review remarks --- ...DataApplication [Local-Standalone].run.xml | 2 +- README.md | 1 + .../folio/linked/data/client/SpecClient.java | 5 ++ .../configuration/CacheConfiguration.java | 21 ----- .../linked/data/job/CacheCleaningJob.java | 19 +++++ .../org/folio/linked/data/util/Constants.java | 4 + .../validation/dto/LccnPatternValidator.java | 21 +++-- .../spec/impl/SpecProviderImpl.java | 12 --- src/main/resources/application.yml | 4 +- .../resource/ResourceControllerITBase.java | 35 ++++++-- .../dto/LccnPatternValidatorTest.java | 82 ++++++++++--------- 11 files changed, 112 insertions(+), 94 deletions(-) delete mode 100644 src/main/java/org/folio/linked/data/configuration/CacheConfiguration.java create mode 100644 src/main/java/org/folio/linked/data/job/CacheCleaningJob.java diff --git a/.run/LinkedDataApplication [Local-Standalone].run.xml b/.run/LinkedDataApplication [Local-Standalone].run.xml index a1699103..b07b7131 100644 --- a/.run/LinkedDataApplication [Local-Standalone].run.xml +++ b/.run/LinkedDataApplication [Local-Standalone].run.xml @@ -13,4 +13,4 @@