From a6e39dbd667c2b4031f5b48a72102a9aed3b1d50 Mon Sep 17 00:00:00 2001 From: Oleksandr Vidinieiev Date: Mon, 27 Jan 2025 17:32:07 +0200 Subject: [PATCH] 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 dd59db4..1ef8742 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 aaf454b..9744c1d 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 0000000..9a37419 --- /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 0000000..64023a4 --- /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 0000000..df0949e --- /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 9604149..36b98d7 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 f2c6485..8203446 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 13c70a9..80b475a 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 f4af93a..8e50d41 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 0000000..ca159f2 --- /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 98d6586..95e4baf 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 76aee0c..06e7a8d 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 d3aea4a..3c8703c 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 06cbd8c..4e9c170 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 edffded..496734d 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 50378fd..a732d70 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 0000000..01031a9 --- /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 5e2978b..d20339a 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 5579278..c804b1c 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))); } }