From a6e39dbd667c2b4031f5b48a72102a9aed3b1d50 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Mon, 27 Jan 2025 17:32:07 +0200 Subject: [PATCH 01/13] MCBFF-44 Create external ECS TLR --- descriptors/ModuleDescriptor-template.json | 9 +- .../client/feign/SearchClient.java | 5 + .../circulationbff/config/TenantConfig.java | 21 +++ .../domain/EcsTenantConfiguration.java | 13 ++ .../EcsTenantConfigurationService.java | 7 + .../circulationbff/service/SearchService.java | 2 + .../service/SettingsService.java | 1 + .../service/UserTenantsService.java | 4 + .../impl/EcsRequestExternalServiceImpl.java | 85 +++++++++- .../EcsTenantConfigurationServiceImpl.java | 34 ++++ .../service/impl/SearchServiceImpl.java | 7 + .../service/impl/SettingsServiceImpl.java | 23 ++- .../service/impl/UserTenantsServiceImpl.java | 37 +++-- src/main/resources/application.yml | 1 + .../swagger.api/circulation-bff.yaml | 2 + .../ecsRequestExternal.yaml | 3 - .../schemas/dto/search/consortiumItem.yaml | 20 +++ .../org/folio/circulationbff/api/BaseIT.java | 2 + .../api/EcsExternalRequestApiTest.java | 152 +++++++++++++----- 19 files changed, 355 insertions(+), 73 deletions(-) create mode 100644 src/main/java/org/folio/circulationbff/config/TenantConfig.java create mode 100644 src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java create mode 100644 src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java create mode 100644 src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java create mode 100644 src/main/resources/swagger.api/schemas/dto/search/consortiumItem.yaml diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index dd59db4c..1ef8742b 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -4,7 +4,7 @@ "provides": [ { "id": "circulation-bff-ecs-request-external", - "version": "1.1", + "version": "1.2", "handlers": [ { "methods": ["POST"], @@ -14,7 +14,12 @@ "tlr.ecs-request-external.post", "user-tenants.collection.get", "circulation.requests.item.get", - "circulation.requests.collection.get" + "circulation.requests.collection.get", + "tlr.settings.get", + "circulation.settings.item.get", + "circulation.settings.collection.get", + "consortium-search.items.collection.get", + "consortium-search.items.item.get" ] } ] diff --git a/src/main/java/org/folio/circulationbff/client/feign/SearchClient.java b/src/main/java/org/folio/circulationbff/client/feign/SearchClient.java index aaf454b8..9744c1d4 100644 --- a/src/main/java/org/folio/circulationbff/client/feign/SearchClient.java +++ b/src/main/java/org/folio/circulationbff/client/feign/SearchClient.java @@ -1,9 +1,11 @@ package org.folio.circulationbff.client.feign; +import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.SearchInstances; import org.folio.spring.config.FeignClientConfiguration; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "search", url = "search", configuration = FeignClientConfiguration.class) @@ -11,4 +13,7 @@ public interface SearchClient { @GetMapping("/instances") SearchInstances findInstances(@RequestParam String query, @RequestParam boolean expandAll); + + @GetMapping("/consortium/item/{itemId}") + ConsortiumItem searchItem(@PathVariable("itemId") String itemId); } diff --git a/src/main/java/org/folio/circulationbff/config/TenantConfig.java b/src/main/java/org/folio/circulationbff/config/TenantConfig.java new file mode 100644 index 00000000..9a374194 --- /dev/null +++ b/src/main/java/org/folio/circulationbff/config/TenantConfig.java @@ -0,0 +1,21 @@ +package org.folio.circulationbff.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import jakarta.annotation.PostConstruct; +import lombok.Data; + +@Configuration +@Data +@ConfigurationProperties("folio.tenant") +public class TenantConfig { + private String secureTenantId; + + @PostConstruct + private void postConstruct() { + if ("${SECURE_TENANT_ID}".equals(secureTenantId)) { + secureTenantId = null; + } + } +} diff --git a/src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java b/src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java new file mode 100644 index 00000000..64023a4d --- /dev/null +++ b/src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java @@ -0,0 +1,13 @@ +package org.folio.circulationbff.domain; + +public record EcsTenantConfiguration(boolean isConsortiaEnabled, String currentTenantId, + String centralTenantId, String secureTenantId) { + + public boolean isCurrentTenantCentral() { + return isConsortiaEnabled && currentTenantId != null && currentTenantId.equals(centralTenantId); + } + + public boolean isCurrentTenantSecure() { + return isConsortiaEnabled && currentTenantId != null && currentTenantId.equals(secureTenantId); + } +} diff --git a/src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java b/src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java new file mode 100644 index 00000000..df0949ee --- /dev/null +++ b/src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java @@ -0,0 +1,7 @@ +package org.folio.circulationbff.service; + +import org.folio.circulationbff.domain.EcsTenantConfiguration; + +public interface EcsTenantConfigurationService { + EcsTenantConfiguration getTenantConfiguration(); +} diff --git a/src/main/java/org/folio/circulationbff/service/SearchService.java b/src/main/java/org/folio/circulationbff/service/SearchService.java index 96041499..36b98d7d 100644 --- a/src/main/java/org/folio/circulationbff/service/SearchService.java +++ b/src/main/java/org/folio/circulationbff/service/SearchService.java @@ -3,7 +3,9 @@ import java.util.Collection; import org.folio.circulationbff.domain.dto.BffSearchInstance; +import org.folio.circulationbff.domain.dto.ConsortiumItem; public interface SearchService { Collection findInstances(String query); + ConsortiumItem findConsortiumItem(String itemId); } diff --git a/src/main/java/org/folio/circulationbff/service/SettingsService.java b/src/main/java/org/folio/circulationbff/service/SettingsService.java index f2c6485d..82034464 100644 --- a/src/main/java/org/folio/circulationbff/service/SettingsService.java +++ b/src/main/java/org/folio/circulationbff/service/SettingsService.java @@ -3,4 +3,5 @@ public interface SettingsService { boolean isEcsTlrFeatureEnabled(); boolean isEcsTlrFeatureEnabled(String tenantId); + boolean isEcsTlrFeatureEnabled(boolean isCentralTenant); } diff --git a/src/main/java/org/folio/circulationbff/service/UserTenantsService.java b/src/main/java/org/folio/circulationbff/service/UserTenantsService.java index 13c70a9c..80b475a1 100644 --- a/src/main/java/org/folio/circulationbff/service/UserTenantsService.java +++ b/src/main/java/org/folio/circulationbff/service/UserTenantsService.java @@ -1,7 +1,11 @@ package org.folio.circulationbff.service; +import org.folio.circulationbff.domain.dto.UserTenant; + public interface UserTenantsService { String getCentralTenant(); boolean isCentralTenant(); + UserTenant getFirstUserTenant(); boolean isCentralTenant(String tenantId); + boolean isCentralTenant(UserTenant userTenant); } diff --git a/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java index f4af93a2..8e50d41c 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java @@ -1,12 +1,20 @@ package org.folio.circulationbff.service.impl; +import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.ITEM; +import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.TITLE; + import org.folio.circulationbff.client.feign.CirculationClient; import org.folio.circulationbff.client.feign.EcsTlrClient; +import org.folio.circulationbff.domain.EcsTenantConfiguration; +import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.EcsRequestExternal; +import org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum; import org.folio.circulationbff.domain.dto.EcsTlr; import org.folio.circulationbff.domain.dto.Request; +import org.folio.circulationbff.service.EcsTenantConfigurationService; import org.folio.circulationbff.service.EcsRequestExternalService; -import org.folio.circulationbff.service.UserTenantsService; +import org.folio.circulationbff.service.SearchService; +import org.folio.circulationbff.service.SettingsService; import org.folio.spring.service.SystemUserScopedExecutionService; import org.springframework.stereotype.Service; @@ -21,18 +29,79 @@ public class EcsRequestExternalServiceImpl implements EcsRequestExternalService private final SystemUserScopedExecutionService systemUserScopedExecutionService; private final EcsTlrClient ecsTlrClient; private final CirculationClient circulationClient; - private final UserTenantsService userTenantsService; - + private final SettingsService settingsService; + private final SearchService searchService; + private final EcsTenantConfigurationService ecsTenantConfigurationService; @Override - public Request createEcsRequestExternal(EcsRequestExternal ecsRequestExternal) { - String centralTenantId = userTenantsService.getCentralTenant(); - log.info("createEcsRequestExternal:: centralTenantId={}", centralTenantId); + public Request createEcsRequestExternal(EcsRequestExternal request) { + log.info("createEcsRequestExternal:: creating external request"); + fetchMissingRequestProperties(request); + EcsTenantConfiguration tenantConfiguration = ecsTenantConfigurationService.getTenantConfiguration(); + + return settingsService.isEcsTlrFeatureEnabled(tenantConfiguration.isCurrentTenantCentral()) + ? createEcsRequest(request, tenantConfiguration) + : createCirculationRequest(request); + } + + private Request createEcsRequest(EcsRequestExternal ecsRequestExternal, + EcsTenantConfiguration tenantConfiguration) { + + log.info("createEcsRequest:: creating ECS request"); + return tenantConfiguration.isCurrentTenantSecure() + ? createMediatedRequest(ecsRequestExternal) + : createExternalEcsTlr(ecsRequestExternal, tenantConfiguration); + } + + private Request createCirculationRequest(EcsRequestExternal ecsRequestExternal) { + log.info("createCirculationRequest:: creating circulation request"); + return ecsRequestExternal.getRequestLevel() == TITLE + ? createTitleLevelRequest(ecsRequestExternal) + : createItemLevelRequest(ecsRequestExternal); + } + + private Request createExternalEcsTlr(EcsRequestExternal ecsRequestExternal, + EcsTenantConfiguration tenantConfiguration) { - EcsTlr ecsTlr = systemUserScopedExecutionService.executeSystemUserScoped(centralTenantId, + log.info("createExternalEcsTlr:: creating ECS TLR"); + EcsTlr ecsTlr = systemUserScopedExecutionService.executeSystemUserScoped( + tenantConfiguration.centralTenantId(), () -> ecsTlrClient.createEcsExternalRequest(ecsRequestExternal)); - log.info("createEcsRequestExternal:: ecsTlr: {}", ecsTlr); + log.info("createExternalEcsTlr:: ECS TLR created: {}", ecsTlr::getId); + log.debug("createExternalEcsTlr:: ecsTlr: {}", ecsTlr); + log.info("createExternalEcsTlr:: fetching primary request"); return circulationClient.getRequestById(ecsTlr.getPrimaryRequestId()); } + + private Request createMediatedRequest(EcsRequestExternal ecsRequestExternal) { + log.info("createMediatedRequest:: creating mediated request"); + // POST /requests-mediated/mediated-requests + return new Request(); + } + + private Request createItemLevelRequest(EcsRequestExternal ecsRequestExternal) { + log.info("createItemLevelRequest:: creating item level request"); + // POST /circulation/requests + return new Request(); + } + + private Request createTitleLevelRequest(EcsRequestExternal ecsRequestExternal) { + log.info("createTitleLevelRequest:: creating title level request"); + // POST /circulation/requests/instances + return new Request(); + } + + private void fetchMissingRequestProperties(EcsRequestExternal request) { + String itemId = request.getItemId(); + RequestLevelEnum requestLevel = request.getRequestLevel(); + log.info("fetchMissingRequestProperties:: requestLevel={}, itemId={}", requestLevel, itemId); + if (requestLevel == ITEM && itemId != null) { + log.info("fetchMissingRequestProperties:: fetching item for item level request"); + ConsortiumItem item = searchService.findConsortiumItem(itemId); + request.instanceId(item.getInstanceId()) + .holdingsRecordId(item.getHoldingsRecordId()); + } + } + } diff --git a/src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java new file mode 100644 index 00000000..ca159f21 --- /dev/null +++ b/src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java @@ -0,0 +1,34 @@ +package org.folio.circulationbff.service.impl; + +import org.folio.circulationbff.config.TenantConfig; +import org.folio.circulationbff.domain.EcsTenantConfiguration; +import org.folio.circulationbff.domain.dto.UserTenant; +import org.folio.circulationbff.service.EcsTenantConfigurationService; +import org.folio.circulationbff.service.UserTenantsService; +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Service +@RequiredArgsConstructor +@Log4j2 +public class EcsTenantConfigurationServiceImpl + implements EcsTenantConfigurationService { + + private final UserTenantsService userTenantsService; + private final TenantConfig tenantConfig; + + @Override + public EcsTenantConfiguration getTenantConfiguration() { + UserTenant userTenant = userTenantsService.getFirstUserTenant(); + + EcsTenantConfiguration tenantConfiguration = userTenant == null + ? new EcsTenantConfiguration(false, null, null, null) + : new EcsTenantConfiguration(true, userTenant.getTenantId(), userTenant.getCentralTenantId(), + tenantConfig.getSecureTenantId()); + + log.info("getTenantConfiguration:: {}", tenantConfiguration); + return tenantConfiguration; + } +} diff --git a/src/main/java/org/folio/circulationbff/service/impl/SearchServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/SearchServiceImpl.java index 98d65863..95e4baf6 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/SearchServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/SearchServiceImpl.java @@ -29,6 +29,7 @@ import org.folio.circulationbff.domain.dto.BffSearchItemLocation; import org.folio.circulationbff.domain.dto.BffSearchItemMaterialType; import org.folio.circulationbff.domain.dto.BffSearchItemStatus; +import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.Contributor; import org.folio.circulationbff.domain.dto.HoldingsRecord; import org.folio.circulationbff.domain.dto.HoldingsRecords; @@ -71,6 +72,12 @@ public class SearchServiceImpl implements SearchService { private final BulkFetchingService fetchingService; private final SearchInstanceMapper searchInstanceMapper; + @Override + public ConsortiumItem findConsortiumItem(String itemId) { + log.info("findConsortiumItem:: looking for item {}", itemId); + return searchClient.searchItem(itemId); + } + @Override public Collection findInstances(String query) { log.info("findInstances:: searching instances by query: {}", query); diff --git a/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java index 76aee0c3..06e7a8d4 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java @@ -21,18 +21,19 @@ public class SettingsServiceImpl implements SettingsService { @Override public boolean isEcsTlrFeatureEnabled() { - if (userTenantsService.isCentralTenant()) { - return ecsTlrClient.getTlrSettings().getEcsTlrFeatureEnabled(); - } - return isTlrEnabledInCirculationSettings(); + return isEcsTlrFeatureEnabled(userTenantsService.isCentralTenant()); } @Override public boolean isEcsTlrFeatureEnabled(String tenantId) { - if (userTenantsService.isCentralTenant(tenantId)) { - return ecsTlrClient.getTlrSettings().getEcsTlrFeatureEnabled(); - } - return isTlrEnabledInCirculationSettings(); + return isEcsTlrFeatureEnabled(userTenantsService.isCentralTenant(tenantId)); + } + + @Override + public boolean isEcsTlrFeatureEnabled(boolean isCentralTenant) { + return isCentralTenant + ? isEcsTlrFeatureEnabledInCentralTenant() + : isTlrEnabledInCirculationSettings(); } private boolean isTlrEnabledInCirculationSettings() { @@ -50,4 +51,10 @@ private boolean isTlrEnabledInCirculationSettings() { } return false; } + + private boolean isEcsTlrFeatureEnabledInCentralTenant() { + Boolean ecsTlrFeatureEnabled = ecsTlrClient.getTlrSettings().getEcsTlrFeatureEnabled(); + log.info("isEcsTlrFeatureEnabled:: {}", ecsTlrFeatureEnabled); + return ecsTlrFeatureEnabled; + } } diff --git a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java index d3aea4aa..3c8703cb 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java @@ -32,20 +32,11 @@ public String getCentralTenant() { @Override public boolean isCentralTenant() { - UserTenant firstUserTenant = getFirstUserTenant(); - if (firstUserTenant == null) { - log.info("isCentralTenant:: failed to fetch user tenants"); - return false; - } - String centralTenantId = firstUserTenant.getCentralTenantId(); - String tenantId = firstUserTenant.getTenantId(); - log.info("isCentralTenant:: centralTenantId={}, tenantId={}", centralTenantId, - tenantId); - - return centralTenantId.equals(tenantId); + return isCentralTenant(getFirstUserTenant()); } - private UserTenant getFirstUserTenant() { + @Override + public UserTenant getFirstUserTenant() { UserTenant firstUserTenant = findFirstUserTenant(); if (firstUserTenant == null) { log.info("processUserGroupEvent: Failed to get user-tenants info"); @@ -67,20 +58,34 @@ public boolean isCentralTenant(String tenantId) { return false; } + @Override + public boolean isCentralTenant(UserTenant userTenant) { + if (userTenant == null) { + log.info("isCentralTenant:: failed to fetch user tenants"); + return false; + } + String centralTenantId = userTenant.getCentralTenantId(); + String tenantId = userTenant.getTenantId(); + log.info("isCentralTenant:: centralTenantId={}, tenantId={}", centralTenantId, + tenantId); + + return centralTenantId.equals(tenantId); + } + private UserTenant findFirstUserTenant() { log.info("findFirstUserTenant:: finding first userTenant"); UserTenant firstUserTenant = null; UserTenantCollection userTenantCollection = userTenantsClient.getUserTenants(1); - log.info("findFirstUserTenant:: userTenantCollection: {}", () -> userTenantCollection); + log.debug("findFirstUserTenant:: userTenantCollection: {}", () -> userTenantCollection); if (userTenantCollection != null) { - log.info("findFirstUserTenant:: userTenantCollection: {}", () -> userTenantCollection); + log.debug("findFirstUserTenant:: userTenantCollection: {}", () -> userTenantCollection); List userTenants = userTenantCollection.getUserTenants(); if (!userTenants.isEmpty()) { firstUserTenant = userTenants.get(0); - log.info("findFirstUserTenant:: found userTenant: {}", firstUserTenant); + log.debug("findFirstUserTenant:: found userTenant: {}", firstUserTenant); } } - log.info("findFirstUserTenant:: result: {}", firstUserTenant); + log.debug("findFirstUserTenant:: result: {}", firstUserTenant); return firstUserTenant; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 06cbd8c7..4e9c1701 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,6 +23,7 @@ spring: enabled: true folio: tenant: + secure-tenant-id: ${SECURE_TENANT_ID} validation: enabled: true environment: ${ENV:folio} diff --git a/src/main/resources/swagger.api/circulation-bff.yaml b/src/main/resources/swagger.api/circulation-bff.yaml index edffdedf..496734d0 100644 --- a/src/main/resources/swagger.api/circulation-bff.yaml +++ b/src/main/resources/swagger.api/circulation-bff.yaml @@ -52,3 +52,5 @@ components: $ref: 'schemas/dto/request/BffRequest.yaml#/BffRequest' ecs-tlr: $ref: 'schemas/dto/ecs-tlr/EcsTlr.yaml#/EcsTlr' + consortium-item: + $ref: 'schemas/dto/search/consortiumItem.yaml' diff --git a/src/main/resources/swagger.api/schemas/dto/ecsRequestExternal/ecsRequestExternal.yaml b/src/main/resources/swagger.api/schemas/dto/ecsRequestExternal/ecsRequestExternal.yaml index 50378fd4..a732d707 100644 --- a/src/main/resources/swagger.api/schemas/dto/ecsRequestExternal/ecsRequestExternal.yaml +++ b/src/main/resources/swagger.api/schemas/dto/ecsRequestExternal/ecsRequestExternal.yaml @@ -58,8 +58,5 @@ properties: type: string required: - - instanceId - requesterId - - requestLevel - - fulfillmentPreference - requestDate \ No newline at end of file diff --git a/src/main/resources/swagger.api/schemas/dto/search/consortiumItem.yaml b/src/main/resources/swagger.api/schemas/dto/search/consortiumItem.yaml new file mode 100644 index 00000000..01031a9d --- /dev/null +++ b/src/main/resources/swagger.api/schemas/dto/search/consortiumItem.yaml @@ -0,0 +1,20 @@ +type: object +properties: + id: + description: Item ID + type: string + hrid: + description: Item HRID + type: string + tenantId: + description: Tenant ID of the Item + type: string + instanceId: + description: Related Instance Id + type: string + holdingsRecordId: + description: Related Holding Record Id + type: string + barcode: + description: Item barcode + type: string \ No newline at end of file diff --git a/src/test/java/org/folio/circulationbff/api/BaseIT.java b/src/test/java/org/folio/circulationbff/api/BaseIT.java index 5e2978b2..d20339a5 100644 --- a/src/test/java/org/folio/circulationbff/api/BaseIT.java +++ b/src/test/java/org/folio/circulationbff/api/BaseIT.java @@ -51,6 +51,7 @@ public class BaseIT { protected static final String TOKEN = "test_token"; protected static final String TENANT_ID_CONSORTIUM = "consortium"; protected static final String TENANT_ID_COLLEGE = "college"; + protected static final String TENANT_ID_SECURE = "secure_tenant"; protected static final String USER_ID = randomId(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL) @@ -90,6 +91,7 @@ public void afterEachTest() { @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("folio.okapi-url", wireMockServer::baseUrl); + registry.add("folio.tenant.secure-tenant-id", () -> TENANT_ID_SECURE); } @SneakyThrows diff --git a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java index 5579278a..c804b1c5 100644 --- a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java +++ b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java @@ -12,15 +12,21 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; import static org.apache.http.HttpStatus.SC_CREATED; import static org.apache.http.HttpStatus.SC_OK; +import static org.folio.circulationbff.domain.dto.EcsRequestExternal.FulfillmentPreferenceEnum.HOLD_SHELF; +import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.ITEM; +import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.TITLE; +import static org.folio.spring.integration.XOkapiHeaders.TENANT; -import java.time.LocalDate; import java.util.Date; import java.util.List; import java.util.UUID; +import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.EcsRequestExternal; +import org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum; import org.folio.circulationbff.domain.dto.EcsTlr; import org.folio.circulationbff.domain.dto.Request; +import org.folio.circulationbff.domain.dto.TlrSettings; import org.folio.circulationbff.domain.dto.UserTenant; import org.folio.circulationbff.domain.dto.UserTenantCollection; import org.folio.spring.integration.XOkapiHeaders; @@ -36,28 +42,77 @@ class EcsExternalRequestApiTest extends BaseIT { "/tlr/create-ecs-request-external"; private static final String CIRCULATION_BFF_CREATE_ECS_EXTERNAL_REQUEST_URL = "/circulation-bff/create-ecs-request-external"; - private static final String CIRCULATION_REQUESTS_URL = "/circulation/requests"; - private static final String TEST_CENTRAL_TENANT_ID = "testCentralTenantId"; + private static final String USER_TENANTS_URL = "/user-tenants"; + private static final String CIRCULATION_REQUEST_URL_TEMPLATE = "/circulation/requests/%s"; + private static final String TLR_SETTINGS_URL = "/tlr/settings"; + private static final String SEARCH_ITEM_URL_TEMPLATE = "/search/consortium/item/%s"; + + private static final String REQUESTER_ID = randomId(); + private static final String ITEM_ID = randomId(); + private static final String INSTANCE_ID = randomId(); + private static final String HOLDING_ID = randomId(); + private static final String PICKUP_SERVICE_POINT_ID = randomId(); + private static final String PRIMARY_REQUEST_ID = randomId(); + private static final Date REQUEST_DATE = new Date(); @Test - @SneakyThrows - void postEcsRequestExternalTest() { - String primaryRequestId = UUID.randomUUID().toString(); - EcsRequestExternal requestExternal = buildEcsRequestExternal(); - mockEcsTlrExternalRequestCreating(requestExternal, primaryRequestId); - mockUserTenants(); - mockPrimaryRequest(primaryRequestId); - mockPerform(requestExternal); + void createExternalItemLevelEcsTlr() { + EcsRequestExternal initialRequest = buildEcsRequestExternal(ITEM); + EcsRequestExternal expectedRequestBody = buildEcsRequestExternal(ITEM) + .holdingsRecordId(HOLDING_ID) + .instanceId(INSTANCE_ID); + + mockItemSearch(ITEM_ID); + mockUserTenants(true); + mockEcsTlrFeatureSettings(true); + mockEcsTlrExternalRequestCreating(expectedRequestBody); + mockPrimaryRequest(); + + createExternalRequest(initialRequest); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, ITEM_ID))) + .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) + .withQueryParam("limit", equalTo("1"))); wireMockServer.verify(1, postRequestedFor(urlPathMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) - .withHeader(XOkapiHeaders.TENANT, equalTo(TEST_CENTRAL_TENANT_ID))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format("%s/%s", - CIRCULATION_REQUESTS_URL, primaryRequestId)))); + .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM)) + .withRequestBody(equalToJson(asJsonString(expectedRequestBody)))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format( + CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID)))); } - private static void mockUserTenants() { - UserTenant userTenant = new UserTenant(); - userTenant.setCentralTenantId(TEST_CENTRAL_TENANT_ID); + @Test + void createExternalTitleLevelEcsTlr() { + EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); + + mockItemSearch(ITEM_ID); + mockUserTenants(true); + mockEcsTlrFeatureSettings(true); + mockEcsTlrExternalRequestCreating(initialRequest); + mockPrimaryRequest(); + + createExternalRequest(initialRequest); + + wireMockServer.verify(0, getRequestedFor(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, ITEM_ID)))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) + .withQueryParam("limit", equalTo("1"))); + + wireMockServer.verify(1, postRequestedFor(urlPathMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) + .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM)) + .withRequestBody(equalToJson(asJsonString(initialRequest)))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format( + CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID)))); + } + + private static void mockUserTenants(boolean isCentralTenant) { + UserTenant userTenant = new UserTenant() + .centralTenantId(TENANT_ID_CONSORTIUM) + .tenantId(isCentralTenant ? TENANT_ID_CONSORTIUM : TENANT_ID_COLLEGE); UserTenantCollection userTenants = new UserTenantCollection(List.of(userTenant), 1); wireMockServer.stubFor(get(urlPathEqualTo(USER_TENANTS_URL)) @@ -66,35 +121,60 @@ private static void mockUserTenants() { .willReturn(jsonResponse(asJsonString(userTenants), SC_OK))); } - private static EcsRequestExternal buildEcsRequestExternal() { - return new EcsRequestExternal( - UUID.randomUUID().toString(), - UUID.randomUUID().toString(), - EcsRequestExternal.RequestLevelEnum.ITEM, - new Date(LocalDate.of(2000, 1, 1).toEpochDay()), - EcsRequestExternal.FulfillmentPreferenceEnum.HOLD_SHELF - ); + private static EcsRequestExternal buildEcsRequestExternal(RequestLevelEnum requestLevel) { + EcsRequestExternal request = new EcsRequestExternal() + .requestLevel(requestLevel) + .requesterId(REQUESTER_ID) + .requestDate(REQUEST_DATE) + .fulfillmentPreference(HOLD_SHELF) + .pickupServicePointId(PICKUP_SERVICE_POINT_ID) + .instanceId(INSTANCE_ID); + + if (requestLevel == ITEM) { + request.setItemId(ITEM_ID); + } else if (requestLevel == TITLE) { + request.setInstanceId(INSTANCE_ID); + } + + return request; } - private void mockPerform(EcsRequestExternal requestExternal) throws Exception { + @SneakyThrows + private void createExternalRequest(EcsRequestExternal requestExternal) { mockMvc.perform(buildRequest(MockMvcRequestBuilders.post( CIRCULATION_BFF_CREATE_ECS_EXTERNAL_REQUEST_URL), requestExternal) - .header(XOkapiHeaders.TENANT, TENANT_ID_CONSORTIUM)); + .header(TENANT, TENANT_ID_CONSORTIUM)); } - private static void mockEcsTlrExternalRequestCreating(EcsRequestExternal requestExternal, - String primaryRequestId) { + private static void mockEcsTlrExternalRequestCreating(EcsRequestExternal requestBody) { + EcsTlr ecsTlr = new EcsTlr() + .id(randomId()) + .primaryRequestId(PRIMARY_REQUEST_ID); wireMockServer.stubFor(WireMock.post(urlMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) - .withRequestBody(equalToJson(asJsonString(requestExternal))) - .willReturn(jsonResponse(asJsonString(new EcsTlr().primaryRequestId(primaryRequestId)), - SC_CREATED))); + .withRequestBody(equalToJson(asJsonString(requestBody))) + .willReturn(jsonResponse(asJsonString(ecsTlr), SC_CREATED))); } - private static void mockPrimaryRequest(String primaryRequestId) { - wireMockServer.stubFor(get(urlMatching(String.format("%s/%s", - CIRCULATION_REQUESTS_URL, primaryRequestId))) + private static void mockPrimaryRequest() { + wireMockServer.stubFor(get(urlMatching(String.format( + CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID))) .willReturn(jsonResponse(asJsonString( - new Request().id(primaryRequestId)), SC_OK))); + new Request().id(PRIMARY_REQUEST_ID)), SC_OK))); + } + + private static void mockEcsTlrFeatureSettings(boolean isEcsTlrEnabled) { + wireMockServer.stubFor(get(urlPathMatching(TLR_SETTINGS_URL)) + .willReturn(jsonResponse(asJsonString(new TlrSettings(isEcsTlrEnabled)), SC_OK))); + } + + private static void mockItemSearch(String itemId) { + ConsortiumItem consortiumItem = new ConsortiumItem() + .id(itemId) + .holdingsRecordId(HOLDING_ID) + .instanceId(INSTANCE_ID); + + wireMockServer.stubFor(get(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, itemId))) + .willReturn(jsonResponse(asJsonString(consortiumItem), SC_OK))); } } From a52088ab3cf87e351f76f873a48cd94442cc6101 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Mon, 27 Jan 2025 17:40:53 +0200 Subject: [PATCH 02/13] MCBFF-44 Add environment variables to README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index de619e6e..1bff5971 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,18 @@ Version 2.0. See the file "[LICENSE](LICENSE)" for more information. Module that provides a facade layer (backed-for-frontend) for FOLIO circulation applications. +### Environment variables + +| Name | Default value | Description | +|:------------------------------|:--------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| JAVA_OPTIONS | -XX:MaxRAMPercentage=66.0 | Java options | +| SYSTEM_USER_USERNAME | mod-circulation-bff | Username for `mod-circulation-bff` system user | +| SYSTEM_USER_PASSWORD | mod-circulation-bff | Password for `mod-circulation-bff` system user | +| SYSTEM_USER_ENABLED | true | Defines if system user must be created at service tenant initialization | +| SECURE_TENANT_ID | - | ID of the secure tenant in a consortia-enabled environment | +| OKAPI_URL | http://okapi:9130 | OKAPI URL used to login system user, required | +| ENV | folio | The logical name of the deployment, must be unique across all environments using the same shared Kafka/Elasticsearch clusters, `a-z (any case)`, `0-9`, `-`, `_` symbols only allowed | + ## Further information ### Issue tracker From 5d4108fa2cc7155c78d74d75fbdef93cd2a5c964 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Tue, 28 Jan 2025 16:04:34 +0200 Subject: [PATCH 03/13] MCBFF-44 TenantService --- .../EcsTenantConfigurationService.java | 7 -- .../circulationbff/service/TenantService.java | 13 +++ .../impl/EcsRequestExternalServiceImpl.java | 28 +++--- .../EcsTenantConfigurationServiceImpl.java | 34 -------- .../service/impl/SettingsServiceImpl.java | 8 +- .../service/impl/TenantServiceImpl.java | 87 +++++++++++++++++++ .../org/folio/circulationbff/api/BaseIT.java | 2 +- .../api/EcsExternalRequestApiTest.java | 33 ++++--- 8 files changed, 138 insertions(+), 74 deletions(-) delete mode 100644 src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java create mode 100644 src/main/java/org/folio/circulationbff/service/TenantService.java delete mode 100644 src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java create mode 100644 src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java diff --git a/src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java b/src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java deleted file mode 100644 index df0949ee..00000000 --- a/src/main/java/org/folio/circulationbff/service/EcsTenantConfigurationService.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.folio.circulationbff.service; - -import org.folio.circulationbff.domain.EcsTenantConfiguration; - -public interface EcsTenantConfigurationService { - EcsTenantConfiguration getTenantConfiguration(); -} diff --git a/src/main/java/org/folio/circulationbff/service/TenantService.java b/src/main/java/org/folio/circulationbff/service/TenantService.java new file mode 100644 index 00000000..968ea126 --- /dev/null +++ b/src/main/java/org/folio/circulationbff/service/TenantService.java @@ -0,0 +1,13 @@ +package org.folio.circulationbff.service; + +import java.util.Optional; + +public interface TenantService { + + String getCurrentTenantId(); + Optional getCentralTenantId(); + Optional getSecureTenantId(); + boolean isCurrentTenantCentral(); + boolean isCurrentTenantSecure(); + boolean isCentralTenant(String tenantId); +} diff --git a/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java index 8e50d41c..1831b3f5 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/EcsRequestExternalServiceImpl.java @@ -5,16 +5,15 @@ import org.folio.circulationbff.client.feign.CirculationClient; import org.folio.circulationbff.client.feign.EcsTlrClient; -import org.folio.circulationbff.domain.EcsTenantConfiguration; import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.EcsRequestExternal; import org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum; import org.folio.circulationbff.domain.dto.EcsTlr; import org.folio.circulationbff.domain.dto.Request; -import org.folio.circulationbff.service.EcsTenantConfigurationService; import org.folio.circulationbff.service.EcsRequestExternalService; import org.folio.circulationbff.service.SearchService; import org.folio.circulationbff.service.SettingsService; +import org.folio.circulationbff.service.TenantService; import org.folio.spring.service.SystemUserScopedExecutionService; import org.springframework.stereotype.Service; @@ -31,26 +30,25 @@ public class EcsRequestExternalServiceImpl implements EcsRequestExternalService private final CirculationClient circulationClient; private final SettingsService settingsService; private final SearchService searchService; - private final EcsTenantConfigurationService ecsTenantConfigurationService; + private final TenantService tenantService; @Override public Request createEcsRequestExternal(EcsRequestExternal request) { - log.info("createEcsRequestExternal:: creating external request"); + log.info("createEcsRequestExternal:: requesterId={}, itemId={}, instanceId={}", + request::getRequesterId, request::getItemId, request::getInstanceId); + fetchMissingRequestProperties(request); - EcsTenantConfiguration tenantConfiguration = ecsTenantConfigurationService.getTenantConfiguration(); - return settingsService.isEcsTlrFeatureEnabled(tenantConfiguration.isCurrentTenantCentral()) - ? createEcsRequest(request, tenantConfiguration) + return settingsService.isEcsTlrFeatureEnabled() + ? createEcsRequest(request) : createCirculationRequest(request); } - private Request createEcsRequest(EcsRequestExternal ecsRequestExternal, - EcsTenantConfiguration tenantConfiguration) { - + private Request createEcsRequest(EcsRequestExternal ecsRequestExternal) { log.info("createEcsRequest:: creating ECS request"); - return tenantConfiguration.isCurrentTenantSecure() + return tenantService.isCurrentTenantSecure() ? createMediatedRequest(ecsRequestExternal) - : createExternalEcsTlr(ecsRequestExternal, tenantConfiguration); + : createExternalEcsTlr(ecsRequestExternal); } private Request createCirculationRequest(EcsRequestExternal ecsRequestExternal) { @@ -60,12 +58,10 @@ private Request createCirculationRequest(EcsRequestExternal ecsRequestExternal) : createItemLevelRequest(ecsRequestExternal); } - private Request createExternalEcsTlr(EcsRequestExternal ecsRequestExternal, - EcsTenantConfiguration tenantConfiguration) { - + private Request createExternalEcsTlr(EcsRequestExternal ecsRequestExternal) { log.info("createExternalEcsTlr:: creating ECS TLR"); EcsTlr ecsTlr = systemUserScopedExecutionService.executeSystemUserScoped( - tenantConfiguration.centralTenantId(), + tenantService.getCentralTenantId().orElseThrow(), () -> ecsTlrClient.createEcsExternalRequest(ecsRequestExternal)); log.info("createExternalEcsTlr:: ECS TLR created: {}", ecsTlr::getId); log.debug("createExternalEcsTlr:: ecsTlr: {}", ecsTlr); diff --git a/src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java deleted file mode 100644 index ca159f21..00000000 --- a/src/main/java/org/folio/circulationbff/service/impl/EcsTenantConfigurationServiceImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.folio.circulationbff.service.impl; - -import org.folio.circulationbff.config.TenantConfig; -import org.folio.circulationbff.domain.EcsTenantConfiguration; -import org.folio.circulationbff.domain.dto.UserTenant; -import org.folio.circulationbff.service.EcsTenantConfigurationService; -import org.folio.circulationbff.service.UserTenantsService; -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Service -@RequiredArgsConstructor -@Log4j2 -public class EcsTenantConfigurationServiceImpl - implements EcsTenantConfigurationService { - - private final UserTenantsService userTenantsService; - private final TenantConfig tenantConfig; - - @Override - public EcsTenantConfiguration getTenantConfiguration() { - UserTenant userTenant = userTenantsService.getFirstUserTenant(); - - EcsTenantConfiguration tenantConfiguration = userTenant == null - ? new EcsTenantConfiguration(false, null, null, null) - : new EcsTenantConfiguration(true, userTenant.getTenantId(), userTenant.getCentralTenantId(), - tenantConfig.getSecureTenantId()); - - log.info("getTenantConfiguration:: {}", tenantConfiguration); - return tenantConfiguration; - } -} diff --git a/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java index 06e7a8d4..4ac092d5 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/SettingsServiceImpl.java @@ -3,7 +3,7 @@ import org.folio.circulationbff.client.feign.CirculationClient; import org.folio.circulationbff.client.feign.EcsTlrClient; import org.folio.circulationbff.service.SettingsService; -import org.folio.circulationbff.service.UserTenantsService; +import org.folio.circulationbff.service.TenantService; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; @@ -17,16 +17,16 @@ public class SettingsServiceImpl implements SettingsService { public static final String ECS_TLR_FEATURE_SETTINGS = "name=ecsTlrFeature"; private final EcsTlrClient ecsTlrClient; private final CirculationClient circulationClient; - private final UserTenantsService userTenantsService; + private final TenantService tenantService; @Override public boolean isEcsTlrFeatureEnabled() { - return isEcsTlrFeatureEnabled(userTenantsService.isCentralTenant()); + return isEcsTlrFeatureEnabled(tenantService.isCurrentTenantCentral()); } @Override public boolean isEcsTlrFeatureEnabled(String tenantId) { - return isEcsTlrFeatureEnabled(userTenantsService.isCentralTenant(tenantId)); + return isEcsTlrFeatureEnabled(tenantService.isCentralTenant(tenantId)); } @Override diff --git a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java new file mode 100644 index 00000000..b1ab36b5 --- /dev/null +++ b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java @@ -0,0 +1,87 @@ +package org.folio.circulationbff.service.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.folio.circulationbff.config.TenantConfig; +import org.folio.circulationbff.domain.dto.UserTenant; +import org.folio.circulationbff.service.TenantService; +import org.folio.circulationbff.service.UserTenantsService; +import org.folio.spring.FolioExecutionContext; +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Service +@RequiredArgsConstructor +@Log4j2 +public class TenantServiceImpl implements TenantService { + + private static final Map CENTRAL_TENANT_ID_CACHE = new HashMap<>(); + + private final UserTenantsService userTenantsService; + private final TenantConfig tenantConfig; + private final FolioExecutionContext folioContext; + + @Override + public String getCurrentTenantId() { + return folioContext.getTenantId(); + } + + @Override + public Optional getCentralTenantId() { + String currentTenantId = getCurrentTenantId(); + String centralTenantId; + + if (CENTRAL_TENANT_ID_CACHE.containsKey(currentTenantId)) { + centralTenantId = CENTRAL_TENANT_ID_CACHE.get(currentTenantId); + log.info("getCentralTenantId:: cache hit: currentTenantId={}, centralTenantId={}", + currentTenantId, centralTenantId); + } else { + log.info("getCentralTenantId:: cache miss: currentTenantId={}", currentTenantId); + centralTenantId = Optional.ofNullable(userTenantsService.getFirstUserTenant()) + .map(UserTenant::getCentralTenantId) + .orElse(null); + + log.info("getCentralTenantId:: populating cache: tenantId={}, centralTenantId={}", + currentTenantId, centralTenantId); + CENTRAL_TENANT_ID_CACHE.put(currentTenantId, centralTenantId); + } + + return Optional.ofNullable(centralTenantId); + } + + @Override + public Optional getSecureTenantId() { + return Optional.ofNullable(tenantConfig.getSecureTenantId()); + } + + @Override + public boolean isCurrentTenantCentral() { + return getCentralTenantId() + .map(getCurrentTenantId()::equals) + .orElse(false); + } + + @Override + public boolean isCurrentTenantSecure() { + return getSecureTenantId() + .map(getCurrentTenantId()::equals) + .orElse(false); + } + + @Override + public boolean isCentralTenant(String tenantId) { + return getCentralTenantId() + .map(tenantId::equals) + .orElse(false); + } + + public static void clearCache() { + log.info("clearCache:: clearing cache"); + CENTRAL_TENANT_ID_CACHE.clear(); + } + +} diff --git a/src/test/java/org/folio/circulationbff/api/BaseIT.java b/src/test/java/org/folio/circulationbff/api/BaseIT.java index d20339a5..fa47cb1f 100644 --- a/src/test/java/org/folio/circulationbff/api/BaseIT.java +++ b/src/test/java/org/folio/circulationbff/api/BaseIT.java @@ -51,7 +51,7 @@ public class BaseIT { protected static final String TOKEN = "test_token"; protected static final String TENANT_ID_CONSORTIUM = "consortium"; protected static final String TENANT_ID_COLLEGE = "college"; - protected static final String TENANT_ID_SECURE = "secure_tenant"; + protected static final String TENANT_ID_SECURE = "secure"; protected static final String USER_ID = randomId(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL) diff --git a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java index c804b1c5..a28224d8 100644 --- a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java +++ b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java @@ -16,10 +16,10 @@ import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.ITEM; import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.TITLE; import static org.folio.spring.integration.XOkapiHeaders.TENANT; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import java.util.Date; import java.util.List; -import java.util.UUID; import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.EcsRequestExternal; @@ -29,9 +29,9 @@ import org.folio.circulationbff.domain.dto.TlrSettings; import org.folio.circulationbff.domain.dto.UserTenant; import org.folio.circulationbff.domain.dto.UserTenantCollection; -import org.folio.spring.integration.XOkapiHeaders; +import org.folio.circulationbff.service.impl.TenantServiceImpl; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import com.github.tomakehurst.wiremock.client.WireMock; @@ -55,6 +55,11 @@ class EcsExternalRequestApiTest extends BaseIT { private static final String PRIMARY_REQUEST_ID = randomId(); private static final Date REQUEST_DATE = new Date(); + @BeforeEach + void beforeEach() { + TenantServiceImpl.clearCache(); + } + @Test void createExternalItemLevelEcsTlr() { EcsRequestExternal initialRequest = buildEcsRequestExternal(ITEM); @@ -63,7 +68,7 @@ void createExternalItemLevelEcsTlr() { .instanceId(INSTANCE_ID); mockItemSearch(ITEM_ID); - mockUserTenants(true); + mockUserTenants(); mockEcsTlrFeatureSettings(true); mockEcsTlrExternalRequestCreating(expectedRequestBody); mockPrimaryRequest(); @@ -89,7 +94,7 @@ void createExternalTitleLevelEcsTlr() { EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); mockItemSearch(ITEM_ID); - mockUserTenants(true); + mockUserTenants(); mockEcsTlrFeatureSettings(true); mockEcsTlrExternalRequestCreating(initialRequest); mockPrimaryRequest(); @@ -109,10 +114,10 @@ void createExternalTitleLevelEcsTlr() { CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID)))); } - private static void mockUserTenants(boolean isCentralTenant) { + private static void mockUserTenants() { UserTenant userTenant = new UserTenant() .centralTenantId(TENANT_ID_CONSORTIUM) - .tenantId(isCentralTenant ? TENANT_ID_CONSORTIUM : TENANT_ID_COLLEGE); + .tenantId("random_tenant"); UserTenantCollection userTenants = new UserTenantCollection(List.of(userTenant), 1); wireMockServer.stubFor(get(urlPathEqualTo(USER_TENANTS_URL)) @@ -127,8 +132,7 @@ private static EcsRequestExternal buildEcsRequestExternal(RequestLevelEnum reque .requesterId(REQUESTER_ID) .requestDate(REQUEST_DATE) .fulfillmentPreference(HOLD_SHELF) - .pickupServicePointId(PICKUP_SERVICE_POINT_ID) - .instanceId(INSTANCE_ID); + .pickupServicePointId(PICKUP_SERVICE_POINT_ID); if (requestLevel == ITEM) { request.setItemId(ITEM_ID); @@ -141,9 +145,14 @@ private static EcsRequestExternal buildEcsRequestExternal(RequestLevelEnum reque @SneakyThrows private void createExternalRequest(EcsRequestExternal requestExternal) { - mockMvc.perform(buildRequest(MockMvcRequestBuilders.post( - CIRCULATION_BFF_CREATE_ECS_EXTERNAL_REQUEST_URL), requestExternal) - .header(TENANT, TENANT_ID_CONSORTIUM)); + createExternalRequest(requestExternal, TENANT_ID_CONSORTIUM); + } + + @SneakyThrows + private void createExternalRequest(EcsRequestExternal requestExternal, String tenantId) { + mockMvc.perform(post(CIRCULATION_BFF_CREATE_ECS_EXTERNAL_REQUEST_URL) + .headers(buildHeaders(tenantId)) + .content(asJsonString(requestExternal))); } private static void mockEcsTlrExternalRequestCreating(EcsRequestExternal requestBody) { From dec5916016f375ad72aca2cb088a24c0f22eb150 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 11:59:00 +0200 Subject: [PATCH 04/13] MCBFF-44 Revert redundant changes --- .../domain/EcsTenantConfiguration.java | 13 ---- .../circulationbff/service/TenantService.java | 1 - .../service/UserTenantsService.java | 5 +- .../service/impl/TenantServiceImpl.java | 18 +++-- .../service/impl/UserTenantsServiceImpl.java | 68 ++++++++----------- 5 files changed, 42 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java diff --git a/src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java b/src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java deleted file mode 100644 index 64023a4d..00000000 --- a/src/main/java/org/folio/circulationbff/domain/EcsTenantConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.folio.circulationbff.domain; - -public record EcsTenantConfiguration(boolean isConsortiaEnabled, String currentTenantId, - String centralTenantId, String secureTenantId) { - - public boolean isCurrentTenantCentral() { - return isConsortiaEnabled && currentTenantId != null && currentTenantId.equals(centralTenantId); - } - - public boolean isCurrentTenantSecure() { - return isConsortiaEnabled && currentTenantId != null && currentTenantId.equals(secureTenantId); - } -} diff --git a/src/main/java/org/folio/circulationbff/service/TenantService.java b/src/main/java/org/folio/circulationbff/service/TenantService.java index 968ea126..a2565d2c 100644 --- a/src/main/java/org/folio/circulationbff/service/TenantService.java +++ b/src/main/java/org/folio/circulationbff/service/TenantService.java @@ -3,7 +3,6 @@ import java.util.Optional; public interface TenantService { - String getCurrentTenantId(); Optional getCentralTenantId(); Optional getSecureTenantId(); diff --git a/src/main/java/org/folio/circulationbff/service/UserTenantsService.java b/src/main/java/org/folio/circulationbff/service/UserTenantsService.java index 80b475a1..2629fcbf 100644 --- a/src/main/java/org/folio/circulationbff/service/UserTenantsService.java +++ b/src/main/java/org/folio/circulationbff/service/UserTenantsService.java @@ -1,11 +1,8 @@ package org.folio.circulationbff.service; -import org.folio.circulationbff.domain.dto.UserTenant; - public interface UserTenantsService { + String getCurrentTenant(); String getCentralTenant(); boolean isCentralTenant(); - UserTenant getFirstUserTenant(); boolean isCentralTenant(String tenantId); - boolean isCentralTenant(UserTenant userTenant); } diff --git a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java index b1ab36b5..98967c39 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java @@ -1,8 +1,8 @@ package org.folio.circulationbff.service.impl; -import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import org.folio.circulationbff.config.TenantConfig; import org.folio.circulationbff.domain.dto.UserTenant; @@ -19,7 +19,7 @@ @Log4j2 public class TenantServiceImpl implements TenantService { - private static final Map CENTRAL_TENANT_ID_CACHE = new HashMap<>(); + private static final Map CENTRAL_TENANT_ID_CACHE = new ConcurrentHashMap<>(); private final UserTenantsService userTenantsService; private final TenantConfig tenantConfig; @@ -41,10 +41,7 @@ public Optional getCentralTenantId() { currentTenantId, centralTenantId); } else { log.info("getCentralTenantId:: cache miss: currentTenantId={}", currentTenantId); - centralTenantId = Optional.ofNullable(userTenantsService.getFirstUserTenant()) - .map(UserTenant::getCentralTenantId) - .orElse(null); - + centralTenantId = resolveCentralTenantId(); log.info("getCentralTenantId:: populating cache: tenantId={}, centralTenantId={}", currentTenantId, centralTenantId); CENTRAL_TENANT_ID_CACHE.put(currentTenantId, centralTenantId); @@ -84,4 +81,13 @@ public static void clearCache() { CENTRAL_TENANT_ID_CACHE.clear(); } + private String resolveCentralTenantId() { + String centralTenantId = Optional.ofNullable(userTenantsService.getFirstUserTenant()) + .map(UserTenant::getCentralTenantId) + .orElse(null); + + log.info("resolveCentralTenantId:: centralTenantId={}", centralTenantId); + return centralTenantId; + } + } diff --git a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java index 3c8703cb..bd62f0f7 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java @@ -1,7 +1,6 @@ package org.folio.circulationbff.service.impl; -import java.util.List; - +import org.apache.commons.collections4.CollectionUtils; import org.folio.circulationbff.client.feign.UserTenantsClient; import org.folio.circulationbff.domain.dto.UserTenant; import org.folio.circulationbff.domain.dto.UserTenantCollection; @@ -18,11 +17,21 @@ public class UserTenantsServiceImpl implements UserTenantsService { private final UserTenantsClient userTenantsClient; + @Override + public String getCurrentTenant() { + UserTenant firstUserTenant = getFirstUserTenant(); + if (firstUserTenant == null) { + return null; + } + String currentTenantId = firstUserTenant.getTenantId(); + log.info("getCurrentTenant:: currentTenantId={}", currentTenantId); + return currentTenantId; + } + @Override public String getCentralTenant() { UserTenant firstUserTenant = getFirstUserTenant(); if (firstUserTenant == null) { - log.info("getCentralTenant:: failed to fetch user tenants"); return null; } String centralTenantId = firstUserTenant.getCentralTenantId(); @@ -32,16 +41,16 @@ public String getCentralTenant() { @Override public boolean isCentralTenant() { - return isCentralTenant(getFirstUserTenant()); - } - - @Override - public UserTenant getFirstUserTenant() { - UserTenant firstUserTenant = findFirstUserTenant(); + UserTenant firstUserTenant = getFirstUserTenant(); if (firstUserTenant == null) { - log.info("processUserGroupEvent: Failed to get user-tenants info"); + return false; } - return firstUserTenant; + String centralTenantId = firstUserTenant.getCentralTenantId(); + String tenantId = firstUserTenant.getTenantId(); + log.info("isCentralTenant:: centralTenantId={}, tenantId={}", centralTenantId, + tenantId); + + return centralTenantId.equals(tenantId); } @Override @@ -58,35 +67,16 @@ public boolean isCentralTenant(String tenantId) { return false; } - @Override - public boolean isCentralTenant(UserTenant userTenant) { - if (userTenant == null) { - log.info("isCentralTenant:: failed to fetch user tenants"); - return false; - } - String centralTenantId = userTenant.getCentralTenantId(); - String tenantId = userTenant.getTenantId(); - log.info("isCentralTenant:: centralTenantId={}, tenantId={}", centralTenantId, - tenantId); - - return centralTenantId.equals(tenantId); - } - - private UserTenant findFirstUserTenant() { - log.info("findFirstUserTenant:: finding first userTenant"); - UserTenant firstUserTenant = null; - UserTenantCollection userTenantCollection = userTenantsClient.getUserTenants(1); - log.debug("findFirstUserTenant:: userTenantCollection: {}", () -> userTenantCollection); - if (userTenantCollection != null) { - log.debug("findFirstUserTenant:: userTenantCollection: {}", () -> userTenantCollection); - List userTenants = userTenantCollection.getUserTenants(); - if (!userTenants.isEmpty()) { - firstUserTenant = userTenants.get(0); - log.debug("findFirstUserTenant:: found userTenant: {}", firstUserTenant); - } + private UserTenant getFirstUserTenant() { + log.info("getFirstUserTenant:: finding first userTenant"); + UserTenantCollection userTenants = userTenantsClient.getUserTenants(1); + log.debug("getFirstUserTenant:: userTenants: {}", () -> userTenants); + if (userTenants == null || CollectionUtils.isEmpty(userTenants.getUserTenants())) { + log.warn("getFirstUserTenant: failed to fetch user tenants"); + return null; } - log.debug("findFirstUserTenant:: result: {}", firstUserTenant); + var firstUserTenant = userTenants.getUserTenants().get(0); + log.debug("getFirstUserTenant:: result: {}", firstUserTenant); return firstUserTenant; } } - From 497975bff8c342c58dfffb7732930a9e5f33f2e8 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 12:21:01 +0200 Subject: [PATCH 05/13] MCBFF-44 Post-merge fixes --- .../org/folio/circulationbff/service/TenantService.java | 1 + .../folio/circulationbff/service/UserTenantsService.java | 3 +++ .../circulationbff/service/impl/TenantServiceImpl.java | 7 +++++++ .../service/impl/UserTenantsServiceImpl.java | 3 ++- .../folio/circulationbff/service/SettingServiceTest.java | 4 ++-- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/folio/circulationbff/service/TenantService.java b/src/main/java/org/folio/circulationbff/service/TenantService.java index a2565d2c..665afb42 100644 --- a/src/main/java/org/folio/circulationbff/service/TenantService.java +++ b/src/main/java/org/folio/circulationbff/service/TenantService.java @@ -9,4 +9,5 @@ public interface TenantService { boolean isCurrentTenantCentral(); boolean isCurrentTenantSecure(); boolean isCentralTenant(String tenantId); + boolean isSecureTenant(String tenantId); } diff --git a/src/main/java/org/folio/circulationbff/service/UserTenantsService.java b/src/main/java/org/folio/circulationbff/service/UserTenantsService.java index 2629fcbf..2d0d1404 100644 --- a/src/main/java/org/folio/circulationbff/service/UserTenantsService.java +++ b/src/main/java/org/folio/circulationbff/service/UserTenantsService.java @@ -1,8 +1,11 @@ package org.folio.circulationbff.service; +import org.folio.circulationbff.domain.dto.UserTenant; + public interface UserTenantsService { String getCurrentTenant(); String getCentralTenant(); boolean isCentralTenant(); boolean isCentralTenant(String tenantId); + UserTenant getFirstUserTenant(); } diff --git a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java index 98967c39..3733c7b9 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java @@ -76,6 +76,13 @@ public boolean isCentralTenant(String tenantId) { .orElse(false); } + @Override + public boolean isSecureTenant(String tenantId) { + return getSecureTenantId() + .map(tenantId::equals) + .orElse(false); + } + public static void clearCache() { log.info("clearCache:: clearing cache"); CENTRAL_TENANT_ID_CACHE.clear(); diff --git a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java index 64472c77..5511073d 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java @@ -67,7 +67,8 @@ public boolean isCentralTenant(String tenantId) { return false; } - private UserTenant getFirstUserTenant() { + @Override + public UserTenant getFirstUserTenant() { log.info("getFirstUserTenant:: finding first userTenant"); UserTenantCollection userTenants = userTenantsClient.getUserTenants(1); log.info("getFirstUserTenant:: userTenants: {}", () -> userTenants); diff --git a/src/test/java/org/folio/circulationbff/service/SettingServiceTest.java b/src/test/java/org/folio/circulationbff/service/SettingServiceTest.java index 690049b7..c34224f3 100644 --- a/src/test/java/org/folio/circulationbff/service/SettingServiceTest.java +++ b/src/test/java/org/folio/circulationbff/service/SettingServiceTest.java @@ -32,7 +32,7 @@ class SettingServiceTest { private CirculationClient circulationClient; @Mock - private UserTenantsService userTenantsService; + private TenantService tenantService; @InjectMocks private SettingsServiceImpl service; @@ -42,7 +42,7 @@ class SettingServiceTest { void isEcsTlrSettingsEnabledTest(boolean isCentralTenant, Boolean isTlrEnabled, Boolean isCirculationTlrEnabled, boolean expectedValue) { - when(userTenantsService.isCentralTenant()).thenReturn(isCentralTenant); + when(tenantService.isCurrentTenantCentral()).thenReturn(isCentralTenant); mockByIsCentralTenantId(isCirculationTlrEnabled, isTlrEnabled, isCentralTenant); assertThat(service.isEcsTlrFeatureEnabled(), equalTo(expectedValue)); From ddd8169bf3afaa361a4c1f045742ddc5b30ec2d2 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 12:51:47 +0200 Subject: [PATCH 06/13] MCBFF-44 More tests --- .../service/impl/UserTenantsServiceImpl.java | 4 +- .../api/EcsExternalRequestApiTest.java | 112 +++++++++++++++--- 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java index 5511073d..7b18554b 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/UserTenantsServiceImpl.java @@ -71,13 +71,13 @@ public boolean isCentralTenant(String tenantId) { public UserTenant getFirstUserTenant() { log.info("getFirstUserTenant:: finding first userTenant"); UserTenantCollection userTenants = userTenantsClient.getUserTenants(1); - log.info("getFirstUserTenant:: userTenants: {}", () -> userTenants); + log.debug("getFirstUserTenant:: userTenants: {}", userTenants); if (userTenants == null || CollectionUtils.isEmpty(userTenants.getUserTenants())) { log.warn("getFirstUserTenant: failed to fetch user tenants"); return null; } var firstUserTenant = userTenants.getUserTenants().get(0); - log.info("getFirstUserTenant:: result: {}", firstUserTenant); + log.debug("getFirstUserTenant:: result: {}", firstUserTenant); return firstUserTenant; } } diff --git a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java index a28224d8..a259303d 100644 --- a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java +++ b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java @@ -5,7 +5,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -17,10 +16,15 @@ import static org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum.TITLE; import static org.folio.spring.integration.XOkapiHeaders.TENANT; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.Date; import java.util.List; +import org.folio.circulationbff.domain.dto.CirculationSettings; +import org.folio.circulationbff.domain.dto.CirculationSettingsResponse; +import org.folio.circulationbff.domain.dto.CirculationSettingsValue; import org.folio.circulationbff.domain.dto.ConsortiumItem; import org.folio.circulationbff.domain.dto.EcsRequestExternal; import org.folio.circulationbff.domain.dto.EcsRequestExternal.RequestLevelEnum; @@ -32,6 +36,9 @@ import org.folio.circulationbff.service.impl.TenantServiceImpl; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import com.github.tomakehurst.wiremock.client.WireMock; @@ -45,6 +52,7 @@ class EcsExternalRequestApiTest extends BaseIT { private static final String USER_TENANTS_URL = "/user-tenants"; private static final String CIRCULATION_REQUEST_URL_TEMPLATE = "/circulation/requests/%s"; private static final String TLR_SETTINGS_URL = "/tlr/settings"; + private static final String CIRCULATION_SETTINGS_URL = "/circulation/settings"; private static final String SEARCH_ITEM_URL_TEMPLATE = "/search/consortium/item/%s"; private static final String REQUESTER_ID = randomId(); @@ -61,19 +69,24 @@ void beforeEach() { } @Test + @SneakyThrows void createExternalItemLevelEcsTlr() { EcsRequestExternal initialRequest = buildEcsRequestExternal(ITEM); EcsRequestExternal expectedRequestBody = buildEcsRequestExternal(ITEM) .holdingsRecordId(HOLDING_ID) .instanceId(INSTANCE_ID); + Request mockPrimaryRequest = new Request().id(PRIMARY_REQUEST_ID); + mockItemSearch(ITEM_ID); mockUserTenants(); mockEcsTlrFeatureSettings(true); mockEcsTlrExternalRequestCreating(expectedRequestBody); - mockPrimaryRequest(); + mockPrimaryRequest(mockPrimaryRequest); - createExternalRequest(initialRequest); + createExternalRequest(initialRequest) + .andExpect(status().isOk()) + .andExpect(content().json(asJsonString(mockPrimaryRequest))); wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, ITEM_ID))) .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM))); @@ -81,6 +94,9 @@ void createExternalItemLevelEcsTlr() { wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); + wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); + wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); + wireMockServer.verify(1, postRequestedFor(urlPathMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM)) .withRequestBody(equalToJson(asJsonString(expectedRequestBody)))); @@ -90,22 +106,28 @@ void createExternalItemLevelEcsTlr() { } @Test + @SneakyThrows void createExternalTitleLevelEcsTlr() { EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); + Request mockPrimaryRequest = new Request().id(PRIMARY_REQUEST_ID); - mockItemSearch(ITEM_ID); mockUserTenants(); mockEcsTlrFeatureSettings(true); mockEcsTlrExternalRequestCreating(initialRequest); - mockPrimaryRequest(); + mockPrimaryRequest(mockPrimaryRequest); - createExternalRequest(initialRequest); + createExternalRequest(initialRequest) + .andExpect(status().isOk()) + .andExpect(content().json(asJsonString(mockPrimaryRequest))); wireMockServer.verify(0, getRequestedFor(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, ITEM_ID)))); wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); + wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); + wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); + wireMockServer.verify(1, postRequestedFor(urlPathMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM)) .withRequestBody(equalToJson(asJsonString(initialRequest)))); @@ -114,6 +136,55 @@ void createExternalTitleLevelEcsTlr() { CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID)))); } + @Test + void createExternalMediatedRequest() { + EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); + + mockUserTenants(); + mockCirculationSettings(true); + + createExternalRequest(initialRequest, TENANT_ID_SECURE); + + wireMockServer.verify(0, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); + wireMockServer.verify(1, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) + .withQueryParam("limit", equalTo("1"))); + } + + @Test + void createExternalTitleLevelCirculationRequest() { + EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); + + mockUserTenants(); + mockEcsTlrFeatureSettings(false); + + createExternalRequest(initialRequest); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); + wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) + .withQueryParam("limit", equalTo("1"))); + } + + @Test + void createExternalItemLevelCirculationRequest() { + EcsRequestExternal initialRequest = buildEcsRequestExternal(ITEM); + + mockItemSearch(ITEM_ID); + mockUserTenants(); + mockEcsTlrFeatureSettings(false); + + createExternalRequest(initialRequest); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); + wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); + + wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) + .withQueryParam("limit", equalTo("1"))); + } + private static void mockUserTenants() { UserTenant userTenant = new UserTenant() .centralTenantId(TENANT_ID_CONSORTIUM) @@ -121,8 +192,7 @@ private static void mockUserTenants() { UserTenantCollection userTenants = new UserTenantCollection(List.of(userTenant), 1); wireMockServer.stubFor(get(urlPathEqualTo(USER_TENANTS_URL)) - .withQueryParam("limit", matching("\\d*")) - .withHeader(HEADER_TENANT, equalTo(TENANT_ID_CONSORTIUM)) + .withQueryParam("limit", equalTo("1")) .willReturn(jsonResponse(asJsonString(userTenants), SC_OK))); } @@ -144,13 +214,13 @@ private static EcsRequestExternal buildEcsRequestExternal(RequestLevelEnum reque } @SneakyThrows - private void createExternalRequest(EcsRequestExternal requestExternal) { - createExternalRequest(requestExternal, TENANT_ID_CONSORTIUM); + private ResultActions createExternalRequest(EcsRequestExternal requestExternal) { + return createExternalRequest(requestExternal, TENANT_ID_CONSORTIUM); } @SneakyThrows - private void createExternalRequest(EcsRequestExternal requestExternal, String tenantId) { - mockMvc.perform(post(CIRCULATION_BFF_CREATE_ECS_EXTERNAL_REQUEST_URL) + private ResultActions createExternalRequest(EcsRequestExternal requestExternal, String tenantId) { + return mockMvc.perform(post(CIRCULATION_BFF_CREATE_ECS_EXTERNAL_REQUEST_URL) .headers(buildHeaders(tenantId)) .content(asJsonString(requestExternal))); } @@ -165,11 +235,10 @@ private static void mockEcsTlrExternalRequestCreating(EcsRequestExternal request .willReturn(jsonResponse(asJsonString(ecsTlr), SC_CREATED))); } - private static void mockPrimaryRequest() { + private static void mockPrimaryRequest(Request mockPrimaryRequest) { wireMockServer.stubFor(get(urlMatching(String.format( CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID))) - .willReturn(jsonResponse(asJsonString( - new Request().id(PRIMARY_REQUEST_ID)), SC_OK))); + .willReturn(jsonResponse(asJsonString(mockPrimaryRequest), SC_OK))); } private static void mockEcsTlrFeatureSettings(boolean isEcsTlrEnabled) { @@ -177,6 +246,19 @@ private static void mockEcsTlrFeatureSettings(boolean isEcsTlrEnabled) { .willReturn(jsonResponse(asJsonString(new TlrSettings(isEcsTlrEnabled)), SC_OK))); } + private static void mockCirculationSettings(boolean isEcsTlrEnabled) { + CirculationSettingsResponse mockResponse = new CirculationSettingsResponse() + .totalRecords(1) + .circulationSettings(List.of(new CirculationSettings() + .id(randomId()) + .name("ecsTlrFeature") + .value(new CirculationSettingsValue().enabled(isEcsTlrEnabled)))); + + wireMockServer.stubFor(get(urlPathMatching(CIRCULATION_SETTINGS_URL)) + .withQueryParam("query", equalTo("name=ecsTlrFeature")) + .willReturn(jsonResponse(asJsonString(mockResponse), SC_OK))); + } + private static void mockItemSearch(String itemId) { ConsortiumItem consortiumItem = new ConsortiumItem() .id(itemId) From f10b51cd97a2c1de9f59f8349341d3b2351ac03f Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 13:52:29 +0200 Subject: [PATCH 07/13] MCBFF-44 Optional values for cache --- .../service/impl/TenantServiceImpl.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java index 3733c7b9..e8daf4ea 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java @@ -19,7 +19,7 @@ @Log4j2 public class TenantServiceImpl implements TenantService { - private static final Map CENTRAL_TENANT_ID_CACHE = new ConcurrentHashMap<>(); + private static final Map> CENTRAL_TENANT_ID_CACHE = new ConcurrentHashMap<>(); private final UserTenantsService userTenantsService; private final TenantConfig tenantConfig; @@ -33,21 +33,22 @@ public String getCurrentTenantId() { @Override public Optional getCentralTenantId() { String currentTenantId = getCurrentTenantId(); - String centralTenantId; + Optional centralTenantId; if (CENTRAL_TENANT_ID_CACHE.containsKey(currentTenantId)) { centralTenantId = CENTRAL_TENANT_ID_CACHE.get(currentTenantId); log.info("getCentralTenantId:: cache hit: currentTenantId={}, centralTenantId={}", - currentTenantId, centralTenantId); + () -> currentTenantId, () -> centralTenantId.orElse(null)); } else { log.info("getCentralTenantId:: cache miss: currentTenantId={}", currentTenantId); - centralTenantId = resolveCentralTenantId(); + centralTenantId = Optional.ofNullable(userTenantsService.getFirstUserTenant()) + .map(UserTenant::getCentralTenantId); log.info("getCentralTenantId:: populating cache: tenantId={}, centralTenantId={}", - currentTenantId, centralTenantId); + () -> currentTenantId, () -> centralTenantId.orElse(null)); CENTRAL_TENANT_ID_CACHE.put(currentTenantId, centralTenantId); } - return Optional.ofNullable(centralTenantId); + return centralTenantId; } @Override @@ -88,13 +89,4 @@ public static void clearCache() { CENTRAL_TENANT_ID_CACHE.clear(); } - private String resolveCentralTenantId() { - String centralTenantId = Optional.ofNullable(userTenantsService.getFirstUserTenant()) - .map(UserTenant::getCentralTenantId) - .orElse(null); - - log.info("resolveCentralTenantId:: centralTenantId={}", centralTenantId); - return centralTenantId; - } - } From 4a73debae8c78a7e1edbddc14b4d4b8b5978c8cb Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 13:52:49 +0200 Subject: [PATCH 08/13] MCBFF-44 Tests for TenantService --- .../api/EcsExternalRequestApiTest.java | 2 - .../service/TenantServiceTest.java | 242 ++++++++++++++++++ 2 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/folio/circulationbff/service/TenantServiceTest.java diff --git a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java index a259303d..d129798f 100644 --- a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java +++ b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java @@ -36,9 +36,7 @@ import org.folio.circulationbff.service.impl.TenantServiceImpl; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import com.github.tomakehurst.wiremock.client.WireMock; diff --git a/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java b/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java new file mode 100644 index 00000000..b747c236 --- /dev/null +++ b/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java @@ -0,0 +1,242 @@ +package org.folio.circulationbff.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.folio.circulationbff.config.TenantConfig; +import org.folio.circulationbff.domain.dto.UserTenant; +import org.folio.circulationbff.service.impl.TenantServiceImpl; +import org.folio.spring.FolioExecutionContext; +import org.junit.jupiter.api.BeforeEach; +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; + +@ExtendWith(MockitoExtension.class) +class TenantServiceTest { + + @Mock + private UserTenantsService userTenantsService; + + @Mock + private TenantConfig tenantConfig; + + @Mock + private FolioExecutionContext folioExecutionContext; + + @InjectMocks + private TenantServiceImpl tenantService; + + @BeforeEach + void setUp() { + TenantServiceImpl.clearCache(); + } + + @Test + void getCurrentTenantIdReturnsTenantIdFromFolioContext() { + when(folioExecutionContext.getTenantId()) + .thenReturn("test_tenant"); + assertEquals("test_tenant", tenantService.getCurrentTenantId()); + } + + @Test + void centralTenantIdIsResolved() { + when(folioExecutionContext.getTenantId()) + .thenReturn("tenant1"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(new UserTenant().centralTenantId("central_tenant")); + + // first invocation, cache is empty + Optional fetchedCentralTenantIdForTenant1 = tenantService.getCentralTenantId(); + assertTrue(fetchedCentralTenantIdForTenant1.isPresent()); + assertEquals("central_tenant", fetchedCentralTenantIdForTenant1.get()); + verify(userTenantsService, times(1)).getFirstUserTenant(); + + // second invocation, central tenant ID is resolved from cache + Optional cachedCentralTenantIdForTenant1 = tenantService.getCentralTenantId(); + assertTrue(cachedCentralTenantIdForTenant1.isPresent()); + assertEquals("central_tenant", cachedCentralTenantIdForTenant1.get()); + verifyNoMoreInteractions(userTenantsService); + + // same for a different tenant + when(folioExecutionContext.getTenantId()) + .thenReturn("tenant2"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(new UserTenant().centralTenantId("central_tenant")); + + Optional fetchedCentralTenantIdForTenant2 = tenantService.getCentralTenantId(); + assertTrue(fetchedCentralTenantIdForTenant2.isPresent()); + assertEquals("central_tenant", fetchedCentralTenantIdForTenant2.get()); + verify(userTenantsService, times(2)).getFirstUserTenant(); + + Optional cachedCentralTenantIdForTenant2 = tenantService.getCentralTenantId(); + assertTrue(cachedCentralTenantIdForTenant2.isPresent()); + assertEquals("central_tenant", cachedCentralTenantIdForTenant2.get()); + verifyNoMoreInteractions(userTenantsService); + } + + @Test + void centralTenantIdIsNotResolved() { + when(folioExecutionContext.getTenantId()) + .thenReturn("tenant1"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(null); + + // first invocation, cache is empty + Optional fetchedCentralTenant = tenantService.getCentralTenantId(); + assertTrue(fetchedCentralTenant.isEmpty()); + verify(userTenantsService, times(1)).getFirstUserTenant(); + + // second invocation, central tenant ID is resolved from cache + Optional cachedCentralTenant = tenantService.getCentralTenantId(); + assertTrue(cachedCentralTenant.isEmpty()); + verifyNoMoreInteractions(userTenantsService); + } + + @Test + void secureTenantIdIsResolved() { + when(tenantConfig.getSecureTenantId()) + .thenReturn("secure_tenant"); + + Optional secureTenantId = tenantService.getSecureTenantId(); + + assertTrue(secureTenantId.isPresent()); + assertEquals("secure_tenant", secureTenantId.get()); + verify(tenantConfig, times(1)).getSecureTenantId(); + } + + @Test + void secureTenantIdIsNotResolved() { + when(tenantConfig.getSecureTenantId()) + .thenReturn(null); + + Optional secureTenantId = tenantService.getSecureTenantId(); + + assertTrue(secureTenantId.isEmpty()); + verify(tenantConfig, times(1)).getSecureTenantId(); + } + + @Test + void isCurrentTenantCentralReturnsTrue() { + when(folioExecutionContext.getTenantId()) + .thenReturn("central_tenant"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(new UserTenant().centralTenantId("central_tenant")); + + assertTrue(tenantService.isCurrentTenantCentral()); + } + + @Test + void isCurrentTenantCentralReturnsFalseWhenTenantIdsDoNotMatch() { + when(folioExecutionContext.getTenantId()) + .thenReturn("random_tenant"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(new UserTenant().centralTenantId("central_tenant")); + + assertFalse(tenantService.isCurrentTenantCentral()); + } + + @Test + void isCurrentTenantCentralReturnsFalseWhenCentralTenantIdIsNotFound() { + when(folioExecutionContext.getTenantId()) + .thenReturn("secure_tenant"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(null); + + assertFalse(tenantService.isCurrentTenantCentral()); + } + + @Test + void isCurrentTenantSecureReturnsTrue() { + when(folioExecutionContext.getTenantId()) + .thenReturn("secure_tenant"); + when(tenantConfig.getSecureTenantId()) + .thenReturn("secure_tenant"); + + assertTrue(tenantService.isCurrentTenantSecure()); + } + + @Test + void isCurrentTenantSecureReturnsFalseWhenTenantIdsDoNotMatch() { + when(folioExecutionContext.getTenantId()) + .thenReturn("random_tenant"); + when(tenantConfig.getSecureTenantId()) + .thenReturn("secure_tenant"); + + assertFalse(tenantService.isCurrentTenantSecure()); + } + + @Test + void isCurrentTenantSecureReturnsFalseWhenSecureTenantIdIsNotFound() { + when(folioExecutionContext.getTenantId()) + .thenReturn("secure_tenant"); + when(tenantConfig.getSecureTenantId()) + .thenReturn(null); + + assertFalse(tenantService.isCurrentTenantSecure()); + } + + @Test + void isCentralTenantReturnsTrue() { + when(folioExecutionContext.getTenantId()) + .thenReturn("random_tenant"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(new UserTenant().centralTenantId("central_tenant")); + + assertTrue(tenantService.isCentralTenant("central_tenant")); + } + + @Test + void isCentralTenantReturnsFalseTenantIdsDoNotMatch() { + when(folioExecutionContext.getTenantId()) + .thenReturn("random_tenant"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(new UserTenant().centralTenantId("central_tenant")); + + assertFalse(tenantService.isCentralTenant("random_tenant")); + } + + @Test + void isCentralTenantReturnsFalseWhenCentralTenantIdIsNotFound() { + when(folioExecutionContext.getTenantId()) + .thenReturn("random_tenant"); + when(userTenantsService.getFirstUserTenant()) + .thenReturn(null); + + assertFalse(tenantService.isCentralTenant("central_tenant")); + } + + @Test + void isSecureTenantReturnsTrue() { + when(tenantConfig.getSecureTenantId()) + .thenReturn("secure_tenant"); + + assertTrue(tenantService.isSecureTenant("secure_tenant")); + } + + @Test + void isSecureTenantReturnsFalseTenantIdsDoNotMatch() { + when(tenantConfig.getSecureTenantId()) + .thenReturn("secure_tenant"); + + assertFalse(tenantService.isSecureTenant("random_tenant")); + } + + @Test + void isSecureTenantReturnsFalseWhenSecureTenantIdIsNotFound() { + when(tenantConfig.getSecureTenantId()) + .thenReturn(null); + + assertFalse(tenantService.isSecureTenant("secure_tenant")); + } + +} \ No newline at end of file From 12fd5d60a087fa8bab4872e6e7e89a0af95313a0 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 13:55:20 +0200 Subject: [PATCH 09/13] MCBFF-44 Remove redundant verifications --- .../org/folio/circulationbff/service/TenantServiceTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java b/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java index b747c236..0d8ad9d9 100644 --- a/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java +++ b/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java @@ -111,7 +111,6 @@ void secureTenantIdIsResolved() { assertTrue(secureTenantId.isPresent()); assertEquals("secure_tenant", secureTenantId.get()); - verify(tenantConfig, times(1)).getSecureTenantId(); } @Test @@ -122,7 +121,6 @@ void secureTenantIdIsNotResolved() { Optional secureTenantId = tenantService.getSecureTenantId(); assertTrue(secureTenantId.isEmpty()); - verify(tenantConfig, times(1)).getSecureTenantId(); } @Test From 336946aa38d7c160d00254e525ad1b3419df2921 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 13:57:06 +0200 Subject: [PATCH 10/13] MCBFF-44 Add missing interface dependency --- descriptors/ModuleDescriptor-template.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 8adb6f51..697d5498 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -259,6 +259,10 @@ "id": "search", "version": "1.3" }, + { + "id": "consortium-search", + "version": "2.1" + }, { "id": "user-tenants", "version": "1.0" From d1ec196b8c6839ffd5c8a1dfecf40d7b1922abc1 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 14:04:55 +0200 Subject: [PATCH 11/13] MCBFF-44 Formatting --- .../api/EcsExternalRequestApiTest.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java index d129798f..a457165b 100644 --- a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java +++ b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java @@ -88,17 +88,13 @@ void createExternalItemLevelEcsTlr() { wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, ITEM_ID))) .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); - wireMockServer.verify(1, postRequestedFor(urlPathMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM)) .withRequestBody(equalToJson(asJsonString(expectedRequestBody)))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format( CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID)))); } @@ -119,54 +115,53 @@ void createExternalTitleLevelEcsTlr() { .andExpect(content().json(asJsonString(mockPrimaryRequest))); wireMockServer.verify(0, getRequestedFor(urlPathMatching(String.format(SEARCH_ITEM_URL_TEMPLATE, ITEM_ID)))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); - wireMockServer.verify(1, postRequestedFor(urlPathMatching(TLR_CREATE_ECS_EXTERNAL_REQUEST_URL)) .withHeader(TENANT, equalTo(TENANT_ID_CONSORTIUM)) .withRequestBody(equalToJson(asJsonString(initialRequest)))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(String.format( CIRCULATION_REQUEST_URL_TEMPLATE, PRIMARY_REQUEST_ID)))); } @Test + @SneakyThrows void createExternalMediatedRequest() { EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); mockUserTenants(); mockCirculationSettings(true); - createExternalRequest(initialRequest, TENANT_ID_SECURE); + createExternalRequest(initialRequest, TENANT_ID_SECURE) + .andExpect(status().isOk()); wireMockServer.verify(0, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); wireMockServer.verify(1, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); } @Test + @SneakyThrows void createExternalTitleLevelCirculationRequest() { EcsRequestExternal initialRequest = buildEcsRequestExternal(TITLE); mockUserTenants(); mockEcsTlrFeatureSettings(false); - createExternalRequest(initialRequest); + createExternalRequest(initialRequest) + .andExpect(status().isOk()); wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); } @Test + @SneakyThrows void createExternalItemLevelCirculationRequest() { EcsRequestExternal initialRequest = buildEcsRequestExternal(ITEM); @@ -174,11 +169,11 @@ void createExternalItemLevelCirculationRequest() { mockUserTenants(); mockEcsTlrFeatureSettings(false); - createExternalRequest(initialRequest); + createExternalRequest(initialRequest) + .andExpect(status().isOk()); wireMockServer.verify(1, getRequestedFor(urlPathMatching(TLR_SETTINGS_URL))); wireMockServer.verify(0, getRequestedFor(urlPathMatching(CIRCULATION_SETTINGS_URL))); - wireMockServer.verify(1, getRequestedFor(urlPathMatching(USER_TENANTS_URL)) .withQueryParam("limit", equalTo("1"))); } From aa87acf8e8e9ccef877f514a5ba6f1f536400364 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 16:46:09 +0200 Subject: [PATCH 12/13] MCBFF-44 Log cache --- .../folio/circulationbff/service/impl/TenantServiceImpl.java | 5 +++-- .../folio/circulationbff/api/EcsExternalRequestApiTest.java | 2 +- .../org/folio/circulationbff/service/TenantServiceTest.java | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java index e8daf4ea..543da00b 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java @@ -47,6 +47,7 @@ public Optional getCentralTenantId() { () -> currentTenantId, () -> centralTenantId.orElse(null)); CENTRAL_TENANT_ID_CACHE.put(currentTenantId, centralTenantId); } + log.debug("getCentralTenantId:: cache: {}", CENTRAL_TENANT_ID_CACHE); return centralTenantId; } @@ -84,8 +85,8 @@ public boolean isSecureTenant(String tenantId) { .orElse(false); } - public static void clearCache() { - log.info("clearCache:: clearing cache"); + public static void clearCentralTenantIdCache() { + log.info("clearCentralTenantIdCache:: clearing cache"); CENTRAL_TENANT_ID_CACHE.clear(); } diff --git a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java index a457165b..5d720080 100644 --- a/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java +++ b/src/test/java/org/folio/circulationbff/api/EcsExternalRequestApiTest.java @@ -63,7 +63,7 @@ class EcsExternalRequestApiTest extends BaseIT { @BeforeEach void beforeEach() { - TenantServiceImpl.clearCache(); + TenantServiceImpl.clearCentralTenantIdCache(); } @Test diff --git a/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java b/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java index 0d8ad9d9..fa239e51 100644 --- a/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java +++ b/src/test/java/org/folio/circulationbff/service/TenantServiceTest.java @@ -1,5 +1,6 @@ package org.folio.circulationbff.service; +import static org.folio.circulationbff.service.impl.TenantServiceImpl.clearCentralTenantIdCache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -38,7 +39,7 @@ class TenantServiceTest { @BeforeEach void setUp() { - TenantServiceImpl.clearCache(); + clearCentralTenantIdCache(); } @Test From 96fa7283b7e26dd397f3f6f8387bc5276382a467 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Wed, 29 Jan 2025 17:23:24 +0200 Subject: [PATCH 13/13] MCBFF-44 Fix log --- .../folio/circulationbff/service/impl/TenantServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java index 543da00b..4c8c4468 100644 --- a/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java +++ b/src/main/java/org/folio/circulationbff/service/impl/TenantServiceImpl.java @@ -37,10 +37,10 @@ public Optional getCentralTenantId() { if (CENTRAL_TENANT_ID_CACHE.containsKey(currentTenantId)) { centralTenantId = CENTRAL_TENANT_ID_CACHE.get(currentTenantId); - log.info("getCentralTenantId:: cache hit: currentTenantId={}, centralTenantId={}", + log.info("getCentralTenantId:: cache hit: tenantId={}, centralTenantId={}", () -> currentTenantId, () -> centralTenantId.orElse(null)); } else { - log.info("getCentralTenantId:: cache miss: currentTenantId={}", currentTenantId); + log.info("getCentralTenantId:: cache miss: tenantId={}", currentTenantId); centralTenantId = Optional.ofNullable(userTenantsService.getFirstUserTenant()) .map(UserTenant::getCentralTenantId); log.info("getCentralTenantId:: populating cache: tenantId={}, centralTenantId={}",