diff --git a/NEWS.md b/NEWS.md
index c477408b3..dfc016141 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -104,6 +104,7 @@
- Create `GET /vocabularies/{name}` API to return a vocabulary used by UI. [MODLD-982](https://folio-org.atlassian.net/browse/MODLD-982)
- Include subTitle (OtherTitleInformation) in work profile [MODLD-1009](https://folio-org.atlassian.net/browse/MODLD-1009)
- Improve export resource query performance [MODLD-1010](https://folio-org.atlassian.net/browse/MODLD-1010)
+- Retrieve FOLIO base-url from `GET /base-url` API [MODLD-804](https://folio-org.atlassian.net/browse/MODLD-804)
## 1.0.4 (04-24-2025)
- Work Edit form - Instance read-only section: "Notes about the instance" data is not shown [MODLD-716](https://folio-org.atlassian.net/browse/MODLD-716)
diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index 68f4a3d97..73dd4f8e5 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -42,7 +42,9 @@
"methods": [ "GET" ],
"pathPattern": "/linked-data/resource/{id}/rdf",
"permissionsRequired": [ "linked-data.resources.rdf.get" ],
- "modulePermissions": []
+ "modulePermissions": [
+ "base-url.item.get"
+ ]
},
{
"methods": [ "PUT" ],
@@ -242,11 +244,15 @@
},
{
"id": "settings",
- "version": "1.1"
+ "version": "1.2"
},
{
"id": "authority-source-files",
"version": "2.2"
+ },
+ {
+ "id": "base-url",
+ "version": "1.0"
}
],
"permissionSets": [
diff --git a/pom.xml b/pom.xml
index aeea1b3d6..ed6535c88 100644
--- a/pom.xml
+++ b/pom.xml
@@ -529,6 +529,7 @@
false
false
false
+ ${project.basedir}/src/main/resources/swagger.api/templates
true
java8
diff --git a/src/main/java/org/folio/linked/data/configuration/HttpClientConfiguration.java b/src/main/java/org/folio/linked/data/configuration/HttpClientConfiguration.java
index 666a29294..58a6386a5 100644
--- a/src/main/java/org/folio/linked/data/configuration/HttpClientConfiguration.java
+++ b/src/main/java/org/folio/linked/data/configuration/HttpClientConfiguration.java
@@ -1,7 +1,6 @@
package org.folio.linked.data.configuration;
import org.folio.linked.data.integration.rest.authoritysource.AuthoritySourceFilesClient;
-import org.folio.linked.data.integration.rest.configuration.ConfigurationClient;
import org.folio.linked.data.integration.rest.search.SearchClient;
import org.folio.linked.data.integration.rest.settings.SettingsClient;
import org.folio.linked.data.integration.rest.specification.SpecClient;
@@ -18,11 +17,6 @@ public AuthoritySourceFilesClient authoritySourceFilesClient(HttpServiceProxyFac
return factory.createClient(AuthoritySourceFilesClient.class);
}
- @Bean
- public ConfigurationClient configurationClient(HttpServiceProxyFactory factory) {
- return factory.createClient(ConfigurationClient.class);
- }
-
@Bean
public SearchClient searchClient(HttpServiceProxyFactory factory) {
return factory.createClient(SearchClient.class);
diff --git a/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationClient.java b/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationClient.java
deleted file mode 100644
index 6e3935856..000000000
--- a/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationClient.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.folio.linked.data.integration.rest.configuration;
-
-import static org.folio.linked.data.util.Constants.Cache.SETTINGS_ENTRIES;
-import static org.folio.linked.data.util.Constants.STANDALONE_PROFILE;
-
-import org.folio.linked.data.domain.dto.Configurations;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.context.annotation.Profile;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.service.annotation.GetExchange;
-import org.springframework.web.service.annotation.HttpExchange;
-
-@HttpExchange("configurations")
-@Profile("!" + STANDALONE_PROFILE)
-public interface ConfigurationClient {
-
- @SuppressWarnings("java:S7180")
- @Cacheable(cacheNames = SETTINGS_ENTRIES, key = "@folioExecutionContext.tenantId + '_' + #code")
- @GetExchange("/entries?query=module=={moduleName} and code=={code}")
- Configurations lookupConfig(@PathVariable("moduleName") String moduleName,
- @PathVariable("code") String code);
-
-}
diff --git a/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationService.java b/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationService.java
deleted file mode 100644
index b8fad31ff..000000000
--- a/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationService.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.folio.linked.data.integration.rest.configuration;
-
-public interface ConfigurationService {
-
- String getFolioHost();
-
-}
diff --git a/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationServiceImpl.java b/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationServiceImpl.java
deleted file mode 100644
index 4606b7e49..000000000
--- a/src/main/java/org/folio/linked/data/integration/rest/configuration/ConfigurationServiceImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.folio.linked.data.integration.rest.configuration;
-
-import static org.folio.linked.data.util.Constants.STANDALONE_PROFILE;
-
-import lombok.RequiredArgsConstructor;
-import org.folio.linked.data.domain.dto.Config;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Service;
-
-@Service
-@RequiredArgsConstructor
-@Profile("!" + STANDALONE_PROFILE)
-public class ConfigurationServiceImpl implements ConfigurationService {
- private static final String MODULE_NAME = "USERSBL";
- private static final String FOLIO_HOST_CONFIG_KEY = "FOLIO_HOST";
- private static final String FOLIO_HOST_DEFAULT = "http://localhost:8081";
- private final ConfigurationClient configurationClient;
-
- @Override
- public String getFolioHost() {
- return configurationClient.lookupConfig(MODULE_NAME, FOLIO_HOST_CONFIG_KEY)
- .getConfigs()
- .stream()
- .map(Config::getValue)
- .findFirst()
- .orElse(FOLIO_HOST_DEFAULT);
- }
-
-}
diff --git a/src/main/java/org/folio/linked/data/integration/rest/settings/SettingsClient.java b/src/main/java/org/folio/linked/data/integration/rest/settings/SettingsClient.java
index 0c3cfcc44..cb4e38865 100644
--- a/src/main/java/org/folio/linked/data/integration/rest/settings/SettingsClient.java
+++ b/src/main/java/org/folio/linked/data/integration/rest/settings/SettingsClient.java
@@ -3,6 +3,7 @@
import static org.folio.linked.data.util.Constants.Cache.SETTINGS_ENTRIES;
import static org.folio.linked.data.util.Constants.STANDALONE_PROFILE;
+import org.folio.linked.data.domain.dto.BaseUrlDto;
import org.folio.linked.data.domain.dto.SettingsSearchResponse;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Profile;
@@ -11,12 +12,17 @@
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
-@HttpExchange("settings")
+@HttpExchange
@Profile("!" + STANDALONE_PROFILE)
public interface SettingsClient {
@SuppressWarnings("java:S7180")
@Cacheable(cacheNames = SETTINGS_ENTRIES, key = "@folioExecutionContext.tenantId + '_' + #query")
- @GetExchange("/entries")
+ @GetExchange("settings/entries")
ResponseEntity getEntries(@RequestParam("query") String query);
+
+ @SuppressWarnings("java:S7180")
+ @Cacheable(cacheNames = SETTINGS_ENTRIES, key = "@folioExecutionContext.tenantId + '_base-url'")
+ @GetExchange("base-url")
+ BaseUrlDto getBaseUrl();
}
diff --git a/src/main/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapper.java b/src/main/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapper.java
index 3b19e149c..c6f12d5fa 100644
--- a/src/main/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapper.java
+++ b/src/main/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapper.java
@@ -22,8 +22,6 @@
@SuppressWarnings("java:S6813")
public abstract class InstanceIngressMessageMapper {
- private static final String LINKED_DATA_ID = "linkedDataId";
- private static final String INSTANCE_ID = "instanceId";
@Autowired
protected Ld2MarcMapper ld2MarcMapper;
@Autowired
@@ -45,9 +43,9 @@ protected String toMarcJson(Resource resource) {
@AfterMapping
protected void afterMappingPayload(@MappingTarget InstanceIngressPayload payload, Resource resource) {
- payload.putAdditionalProperty(LINKED_DATA_ID, resource.getId());
+ payload.setLinkedDataId(resource.getId());
ofNullable(resource.getFolioMetadata())
.map(FolioMetadata::getInventoryId)
- .ifPresent(invId -> payload.putAdditionalProperty(INSTANCE_ID, invId));
+ .ifPresent(payload::setInstanceId);
}
}
diff --git a/src/main/java/org/folio/linked/data/service/rdf/ResourceUrlProvider.java b/src/main/java/org/folio/linked/data/service/rdf/ResourceUrlProvider.java
index f85ed354a..8b4ca7c0c 100644
--- a/src/main/java/org/folio/linked/data/service/rdf/ResourceUrlProvider.java
+++ b/src/main/java/org/folio/linked/data/service/rdf/ResourceUrlProvider.java
@@ -4,7 +4,7 @@
import java.util.function.LongFunction;
import lombok.RequiredArgsConstructor;
-import org.folio.linked.data.integration.rest.configuration.ConfigurationService;
+import org.folio.linked.data.integration.rest.settings.SettingsClient;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@@ -14,12 +14,20 @@
public class ResourceUrlProvider implements LongFunction {
private static final String URL_PATTERN = "%s/linked-data-editor/resources/%s";
- private final ConfigurationService configurationService;
-
+ private final SettingsClient settingsClient;
@Override
public String apply(long id) {
- var folioHost = configurationService.getFolioHost();
- return String.format(URL_PATTERN, folioHost, id);
+ var baseUrlResponse = settingsClient.getBaseUrl();
+ var baseUrl = normalizeBaseUrl(baseUrlResponse.getValue());
+ return String.format(URL_PATTERN, baseUrl, id);
+ }
+
+ private String normalizeBaseUrl(String baseUrl) {
+ var normalized = baseUrl;
+ while (normalized.endsWith("/")) {
+ normalized = normalized.substring(0, normalized.length() - 1);
+ }
+ return normalized;
}
}
diff --git a/src/main/resources/swagger.api/folio-modules.yaml b/src/main/resources/swagger.api/folio-modules.yaml
index 3017131e8..764b3e1f7 100644
--- a/src/main/resources/swagger.api/folio-modules.yaml
+++ b/src/main/resources/swagger.api/folio-modules.yaml
@@ -32,8 +32,8 @@ components:
$ref: folio-modules/srs/sourceRecordDomainEvent.json
inventoryInstanceEvent:
$ref: folio-modules/inventory/inventoryInstanceEvent.json
- configurationsDto:
- $ref: folio-modules/configuration/configurations.json
+ baseUrlDto:
+ $ref: folio-modules/settings/baseUrl.json
importOutputEvent:
$ref: folio-modules/ld-import/importOutputEvent.json
importResultEvent:
diff --git a/src/main/resources/swagger.api/folio-modules/configuration/config.json b/src/main/resources/swagger.api/folio-modules/configuration/config.json
deleted file mode 100644
index b345bf86f..000000000
--- a/src/main/resources/swagger.api/folio-modules/configuration/config.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type": "object",
- "description": "mod-configuration DTO for a single configuration",
- "properties": {
- "id": {
- "type": "string",
- "description": "ID of the configuration record",
- "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
- },
- "code": {
- "type": "string",
- "description": "Configuration code (key)"
- },
- "value": {
- "type": "string",
- "description": "Configuration value"
- }
- },
- "additionalProperties": false,
- "required": [
- "code",
- "value"
- ]
-}
diff --git a/src/main/resources/swagger.api/folio-modules/configuration/configurations.json b/src/main/resources/swagger.api/folio-modules/configuration/configurations.json
deleted file mode 100644
index 472f733e4..000000000
--- a/src/main/resources/swagger.api/folio-modules/configuration/configurations.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-04/schema#",
- "type": "object",
- "description": "mod-configuration DTO for multiple configurations",
- "properties": {
- "configs": {
- "description": "configurationData",
- "type": "array",
- "items": {
- "type": "object",
- "$ref": "config.json"
- }
- },
- "totalRecords": {
- "type": "integer"
- }
- },
- "additionalProperties": false,
- "required": [
- "configs",
- "totalRecords"
- ]
-}
diff --git a/src/main/resources/swagger.api/folio-modules/inventory/instanceIngressPayload.json b/src/main/resources/swagger.api/folio-modules/inventory/instanceIngressPayload.json
index 3533b307a..0a3a31bd7 100644
--- a/src/main/resources/swagger.api/folio-modules/inventory/instanceIngressPayload.json
+++ b/src/main/resources/swagger.api/folio-modules/inventory/instanceIngressPayload.json
@@ -15,9 +15,17 @@
"type": "string",
"enum": ["FOLIO", "LINKED_DATA", "MARC"],
"description": "Source type"
+ },
+ "linkedDataId": {
+ "type": "integer",
+ "format": "int64",
+ "description": "Linked data resource identifier"
+ },
+ "instanceId": {
+ "type": "string",
+ "description": "Inventory instance identifier"
}
},
- "additionalProperties": true,
"required": [
"sourceRecordObject",
"sourceType"
diff --git a/src/main/resources/swagger.api/folio-modules/settings/baseUrl.json b/src/main/resources/swagger.api/folio-modules/settings/baseUrl.json
new file mode 100644
index 000000000..c8f5a39d2
--- /dev/null
+++ b/src/main/resources/swagger.api/folio-modules/settings/baseUrl.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "description": "Base URL response",
+ "properties": {
+ "value": {
+ "type": "string",
+ "description": "FOLIO front-end URL",
+ "x-json-property": "baseUrl"
+ }
+ },
+ "required": [
+ "value"
+ ]
+}
diff --git a/src/main/resources/swagger.api/folio-modules/srs/record/parsedRecord.json b/src/main/resources/swagger.api/folio-modules/srs/record/parsedRecord.json
index 8f5e32b04..2d16b8a36 100644
--- a/src/main/resources/swagger.api/folio-modules/srs/record/parsedRecord.json
+++ b/src/main/resources/swagger.api/folio-modules/srs/record/parsedRecord.json
@@ -2,7 +2,6 @@
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Parsed Record Schema",
"type": "object",
- "additionalProperties": false,
"properties": {
"id": {
"description": "UUID",
diff --git a/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecord.json b/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecord.json
index a6f589034..a5637b82a 100644
--- a/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecord.json
+++ b/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecord.json
@@ -2,7 +2,6 @@
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Record DTO Schema",
"type": "object",
- "additionalProperties": false,
"properties": {
"id": {
"description": "UUID",
diff --git a/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordMetadata.json b/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordMetadata.json
index b4d84e596..8ae1e91b9 100644
--- a/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordMetadata.json
+++ b/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordMetadata.json
@@ -33,7 +33,6 @@
"type": "string"
}
},
- "additionalProperties": false,
"required": [
"createdDate"
]
diff --git a/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordType.json b/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordType.json
index 23f20c979..b39a93fb4 100644
--- a/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordType.json
+++ b/src/main/resources/swagger.api/folio-modules/srs/record/sourceRecordType.json
@@ -2,7 +2,6 @@
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Record Type Enum",
"type": "string",
- "additionalProperties": false,
"enum": [
"MARC_BIB",
"MARC_AUTHORITY",
diff --git a/src/main/resources/swagger.api/folio-modules/srs/sourceRecordDomainEvent.json b/src/main/resources/swagger.api/folio-modules/srs/sourceRecordDomainEvent.json
index bd14c174f..9e95e90dd 100644
--- a/src/main/resources/swagger.api/folio-modules/srs/sourceRecordDomainEvent.json
+++ b/src/main/resources/swagger.api/folio-modules/srs/sourceRecordDomainEvent.json
@@ -3,7 +3,6 @@
"description": "Source record domain event data model",
"javaType": "org.folio.rest.jaxrs.model.SourceRecordDomainEvent",
"type": "object",
- "additionalProperties": false,
"properties": {
"id": {
"description": "UUID",
diff --git a/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerUpdateWorkIT.java b/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerUpdateWorkIT.java
index 668e651c9..2d950ca90 100644
--- a/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerUpdateWorkIT.java
+++ b/src/test/java/org/folio/linked/data/e2e/resource/ResourceControllerUpdateWorkIT.java
@@ -213,7 +213,7 @@ private boolean isExpectedEvent(String eventStr, long linkedDataId) {
var eventPayload = event.getEventPayload();
var marc = eventPayload.getSourceRecordObject();
return event.getEventType() == InstanceIngressEvent.EventTypeEnum.UPDATE_INSTANCE
- && eventPayload.getAdditionalProperties().get("linkedDataId").equals(linkedDataId)
+ && eventPayload.getLinkedDataId().equals(linkedDataId)
&& isExpectedMarc(marc);
}
diff --git a/src/test/java/org/folio/linked/data/integration/rest/configuration/ConfigurationClientCacheIT.java b/src/test/java/org/folio/linked/data/integration/rest/configuration/ConfigurationClientCacheIT.java
deleted file mode 100644
index cc81782dd..000000000
--- a/src/test/java/org/folio/linked/data/integration/rest/configuration/ConfigurationClientCacheIT.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.folio.linked.data.integration.rest.configuration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.folio.linked.data.test.TestUtil.TENANT_ID;
-import static org.folio.linked.data.util.Constants.Cache.SETTINGS_ENTRIES;
-
-import org.folio.linked.data.e2e.base.IntegrationTest;
-import org.folio.linked.data.service.tenant.TenantScopedExecutionService;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cache.CacheManager;
-
-@IntegrationTest
-class ConfigurationClientCacheIT {
-
- @Autowired
- private ConfigurationService configurationService;
- @Autowired
- private CacheManager cacheManager;
- @Autowired
- private TenantScopedExecutionService tenantScopedExecutionService;
-
- @BeforeEach
- void setUp() {
- clearCache();
- }
-
- @AfterEach
- void tearDown() {
- clearCache();
- }
-
- @Test
- void shouldNotShareCacheAcrossTenants() {
- // given
- var tenant1 = TENANT_ID;
-
- // when - populate cache for tenant1
- tenantScopedExecutionService.execute(tenant1, () -> configurationService.getFolioHost());
-
- // Verify tenant1 has cache entry
- var cache = cacheManager.getCache(SETTINGS_ENTRIES);
- assertThat(cache).isNotNull();
- var tenant1CacheKey = tenant1 + "_FOLIO_HOST";
- assertThat(cache.get(tenant1CacheKey)).isNotNull();
-
- // when - check tenant2 cache before any call
- var tenant2 = "another_tenant";
- var tenant2CacheKey = tenant2 + "_FOLIO_HOST";
- var tenant2CacheValue = cache.get(tenant2CacheKey);
-
- // then - tenant2 should not have any cached value yet
- assertThat(tenant2CacheValue).isNull();
-
- // when - make call for tenant2
- tenantScopedExecutionService.execute(tenant2, () -> configurationService.getFolioHost());
-
- // then - now tenant2 should have its own cache entry
- assertThat(cache.get(tenant2CacheKey)).isNotNull();
-
- // Verify both tenants have separate cache entries
- assertThat(cache.get(tenant1CacheKey)).isNotNull();
- assertThat(cache.get(tenant2CacheKey)).isNotNull();
-
- // Verify they are different cache entries
- assertThat(cache.get(tenant1CacheKey)).isNotEqualTo(cache.get(tenant2CacheKey));
- }
-
-
- private void clearCache() {
- var cache = cacheManager.getCache(SETTINGS_ENTRIES);
- if (cache != null) {
- cache.clear();
- }
- }
-}
-
diff --git a/src/test/java/org/folio/linked/data/integration/rest/configuration/ConfigurationServiceTest.java b/src/test/java/org/folio/linked/data/integration/rest/configuration/ConfigurationServiceTest.java
deleted file mode 100644
index 451e9a207..000000000
--- a/src/test/java/org/folio/linked/data/integration/rest/configuration/ConfigurationServiceTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.folio.linked.data.integration.rest.configuration;
-
-import static java.util.Objects.nonNull;
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.mockito.Mockito.when;
-
-import java.util.List;
-import org.folio.linked.data.domain.dto.Config;
-import org.folio.linked.data.domain.dto.Configurations;
-import org.folio.spring.testing.type.UnitTest;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-@UnitTest
-@ExtendWith(MockitoExtension.class)
-class ConfigurationServiceTest {
-
- @InjectMocks
- private ConfigurationServiceImpl configurationService;
- @Mock
- private ConfigurationClient configurationClient;
-
- @Test
- void getFolioHost_returnsFolioHostFromConfigs() {
- // given
- var folioHost = "http://example.com";
- var configurations = getConfigurations("FOLIO_HOST", folioHost);
- when(configurationClient.lookupConfig("USERSBL", "FOLIO_HOST")).thenReturn(configurations);
-
- // when
- var result = configurationService.getFolioHost();
-
- // then
- assertThat(result).isEqualTo(folioHost);
- }
-
- @Test
- void getFolioHost_returnsDefaultFolioHostWhenConfigsAreEmpty() {
- when(configurationClient.lookupConfig("USERSBL", "FOLIO_HOST"))
- .thenReturn(getConfigurations(null, null));
-
- var result = configurationService.getFolioHost();
-
- assertThat(result).isEqualTo("http://localhost:8081");
- }
-
- private Configurations getConfigurations(String key, String value) {
- var configurations = new Configurations();
- if (nonNull(key)) {
- var config = new Config();
- config.setCode(key);
- config.setValue(value);
- configurations.setConfigs(List.of(config));
- }
- configurations.setTotalRecords(1);
- return configurations;
- }
-}
diff --git a/src/test/java/org/folio/linked/data/integration/rest/settings/SettingsClientCacheIT.java b/src/test/java/org/folio/linked/data/integration/rest/settings/SettingsClientCacheIT.java
index c073d3140..5426077d7 100644
--- a/src/test/java/org/folio/linked/data/integration/rest/settings/SettingsClientCacheIT.java
+++ b/src/test/java/org/folio/linked/data/integration/rest/settings/SettingsClientCacheIT.java
@@ -33,7 +33,7 @@ void tearDown() {
}
@Test
- void shouldNotShareCacheAcrossTenants() {
+ void shouldNotShareEntriesCacheAcrossTenants() {
// given
var tenant1 = TENANT_ID;
var query = "key==test.setting";
@@ -62,11 +62,43 @@ void shouldNotShareCacheAcrossTenants() {
assertThat(cache.get(tenant2CacheKey)).isNotNull();
// Verify both tenants have separate cache entries with different keys
+ assertThat(cache.get(tenant1CacheKey).get()).isNotNull();
+ assertThat(cache.get(tenant2CacheKey).get()).isNotNull();
+ assertThat(cache.get(tenant1CacheKey).get()).isNotEqualTo(cache.get(tenant2CacheKey).get());
+ }
+
+ @Test
+ void shouldNotShareBaseUrlCacheAcrossTenants() {
+ // given
+ var tenant1 = TENANT_ID;
+
+ // when - populate cache for tenant1
+ tenantScopedExecutionService.execute(tenant1, () -> settingsClient.getBaseUrl());
+
+ // Verify tenant1 has cache entry
+ var cache = cacheManager.getCache(SETTINGS_ENTRIES);
+ assertThat(cache).isNotNull();
+ var tenant1CacheKey = tenant1 + "_base-url";
assertThat(cache.get(tenant1CacheKey)).isNotNull();
+
+ // when - check tenant2 cache before any call
+ var tenant2 = "another_tenant";
+ var tenant2CacheKey = tenant2 + "_base-url";
+ var tenant2CacheValue = cache.get(tenant2CacheKey);
+
+ // then - tenant2 should not have any cached value yet
+ assertThat(tenant2CacheValue).isNull();
+
+ // when - make call for tenant2
+ tenantScopedExecutionService.execute(tenant2, () -> settingsClient.getBaseUrl());
+
+ // then - now tenant2 should have its own cache entry
assertThat(cache.get(tenant2CacheKey)).isNotNull();
- // Verify that the cache keys are indeed different
- assertThat(tenant1CacheKey).isNotEqualTo(tenant2CacheKey);
+ // Verify both tenants have separate cache entries
+ assertThat(cache.get(tenant1CacheKey).get()).isNotNull();
+ assertThat(cache.get(tenant2CacheKey).get()).isNotNull();
+ assertThat(cache.get(tenant1CacheKey).get()).isNotEqualTo(cache.get(tenant2CacheKey).get());
}
private void clearCache() {
@@ -76,4 +108,3 @@ private void clearCache() {
}
}
}
-
diff --git a/src/test/java/org/folio/linked/data/integration/rest/specification/SpecClientCacheIT.java b/src/test/java/org/folio/linked/data/integration/rest/specification/SpecClientCacheIT.java
index ff0b8517c..7244b5ec6 100644
--- a/src/test/java/org/folio/linked/data/integration/rest/specification/SpecClientCacheIT.java
+++ b/src/test/java/org/folio/linked/data/integration/rest/specification/SpecClientCacheIT.java
@@ -63,11 +63,10 @@ void shouldNotShareCacheAcrossTenants() {
assertThat(cache.get(tenant2CacheKey)).isNotNull();
// Verify both tenants have separate cache entries with different keys
- assertThat(cache.get(tenant1CacheKey)).isNotNull();
- assertThat(cache.get(tenant2CacheKey)).isNotNull();
-
- // Verify that the cache keys are indeed different
assertThat(tenant1CacheKey).isNotEqualTo(tenant2CacheKey);
+ assertThat(cache.get(tenant1CacheKey).get()).isNotNull();
+ assertThat(cache.get(tenant2CacheKey).get()).isNotNull();
+ assertThat(cache.get(tenant1CacheKey).get()).isNotEqualTo(cache.get(tenant2CacheKey).get());
}
private void clearCaches() {
@@ -81,4 +80,3 @@ private void clearCaches() {
}
}
}
-
diff --git a/src/test/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapperTest.java b/src/test/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapperTest.java
index 87423685c..aa2760220 100644
--- a/src/test/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapperTest.java
+++ b/src/test/java/org/folio/linked/data/mapper/kafka/inventory/InstanceIngressMessageMapperTest.java
@@ -5,7 +5,6 @@
import static org.folio.linked.data.test.TestUtil.randomLong;
import static org.mockito.Mockito.doReturn;
-import java.util.Map;
import java.util.UUID;
import org.folio.linked.data.domain.dto.InstanceIngressPayload;
import org.folio.linked.data.mapper.ResourceModelMapper;
@@ -59,9 +58,8 @@ void testMapping() {
.hasFieldOrPropertyWithValue("sourceRecordIdentifier", srsId)
.hasFieldOrPropertyWithValue("sourceType", InstanceIngressPayload.SourceTypeEnum.LINKED_DATA)
.hasFieldOrPropertyWithValue("sourceRecordObject", marcString)
- .hasFieldOrPropertyWithValue("additionalProperties",
- Map.of("linkedDataId", instance.getId(), "instanceId", inventoryId)
- );
+ .hasFieldOrPropertyWithValue("linkedDataId", instance.getId())
+ .hasFieldOrPropertyWithValue("instanceId", inventoryId);
}
}
diff --git a/src/test/java/org/folio/linked/data/service/rdf/ResourceUrlProviderTest.java b/src/test/java/org/folio/linked/data/service/rdf/ResourceUrlProviderTest.java
index 273e4578c..50731a2f7 100644
--- a/src/test/java/org/folio/linked/data/service/rdf/ResourceUrlProviderTest.java
+++ b/src/test/java/org/folio/linked/data/service/rdf/ResourceUrlProviderTest.java
@@ -3,7 +3,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
-import org.folio.linked.data.integration.rest.configuration.ConfigurationService;
+import org.folio.linked.data.domain.dto.BaseUrlDto;
+import org.folio.linked.data.integration.rest.settings.SettingsClient;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -19,11 +20,11 @@ class ResourceUrlProviderTest {
@InjectMocks
private ResourceUrlProvider resourceUrlProvider;
@Mock
- private ConfigurationService configurationService;
+ private SettingsClient settingsClient;
@BeforeEach
void setUp() {
- when(configurationService.getFolioHost()).thenReturn("http://localhost");
+ when(settingsClient.getBaseUrl()).thenReturn(new BaseUrlDto("http://localhost"));
}
@Test
@@ -39,4 +40,18 @@ void returnsCorrectUrlForValidId() {
assertThat(result).isEqualTo(expectedUrl);
}
+ @Test
+ void returnsCorrectUrlWhenBaseUrlHasTrailingSlash() {
+ // given
+ when(settingsClient.getBaseUrl()).thenReturn(new BaseUrlDto("http://localhost/"));
+ var id = 123L;
+ var expectedUrl = "http://localhost/linked-data-editor/resources/123";
+
+ // when
+ var result = resourceUrlProvider.apply(id);
+
+ // then
+ assertThat(result).isEqualTo(expectedUrl);
+ }
+
}
diff --git a/src/test/resources/mappings/base-url.json b/src/test/resources/mappings/base-url.json
new file mode 100644
index 000000000..f966c2403
--- /dev/null
+++ b/src/test/resources/mappings/base-url.json
@@ -0,0 +1,40 @@
+{
+ "mappings": [
+ {
+ "request": {
+ "method": "GET",
+ "url": "/base-url",
+ "headers": {
+ "X-Okapi-Tenant": {
+ "equalTo": "test_tenant"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json"
+ },
+ "body": "{\n \"baseUrl\": \"https://folio-etesting-snapshot-diku.ci.folio.org\"\n}"
+ }
+ },
+ {
+ "request": {
+ "method": "GET",
+ "url": "/base-url",
+ "headers": {
+ "X-Okapi-Tenant": {
+ "equalTo": "another_tenant"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json"
+ },
+ "body": "{\n \"baseUrl\": \"https://another-tenant-folio.example.com\"\n}"
+ }
+ }
+ ]
+}
diff --git a/src/test/resources/mappings/configurations.json b/src/test/resources/mappings/configurations.json
deleted file mode 100644
index e29bd6daa..000000000
--- a/src/test/resources/mappings/configurations.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "mappings": [
- {
- "request": {
- "method": "GET",
- "url": "/configurations/entries?query=module%3D%3DUSERSBL%20and%20code%3D%3DFOLIO_HOST",
- "headers": {
- "X-Okapi-Tenant": {
- "equalTo": "test_tenant"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json"
- },
- "body": "{\n \"configs\": [\n {\n \"id\": \"bad0361b-7918-42fe-9bc0-e8901cf2a4f3\",\n \"module\": \"USERSBL\",\n \"configName\": \"FOLIO host\",\n \"code\": \"FOLIO_HOST\",\n \"description\": \"FOLIO host address for password reset\",\n \"default\": true,\n \"enabled\": true,\n \"value\": \"https://folio-etesting-snapshot-diku.ci.folio.org\",\n \"metadata\": {\n \"createdDate\": \"2025-08-25T03:22:38.750+00:00\",\n \"updatedDate\": \"2025-08-25T03:22:38.750+00:00\"\n }\n }\n ],\n \"totalRecords\": 1,\n \"resultInfo\": {\n \"totalRecords\": 1,\n \"facets\": [],\n \"diagnostics\": []\n }\n}"
- }
- },
- {
- "request": {
- "method": "GET",
- "url": "/configurations/entries?query=module%3D%3DUSERSBL%20and%20code%3D%3DFOLIO_HOST",
- "headers": {
- "X-Okapi-Tenant": {
- "equalTo": "another_tenant"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json"
- },
- "body": "{\n \"configs\": [\n {\n \"id\": \"cfe1472c-8919-53af-0cd1-f9902dg3b5g4\",\n \"module\": \"USERSBL\",\n \"configName\": \"FOLIO host\",\n \"code\": \"FOLIO_HOST\",\n \"description\": \"FOLIO host address for password reset\",\n \"default\": true,\n \"enabled\": true,\n \"value\": \"https://another-tenant-folio.example.com\",\n \"metadata\": {\n \"createdDate\": \"2025-01-26T10:00:00.000+00:00\",\n \"updatedDate\": \"2025-01-26T10:00:00.000+00:00\"\n }\n }\n ],\n \"totalRecords\": 1,\n \"resultInfo\": {\n \"totalRecords\": 1,\n \"facets\": [],\n \"diagnostics\": []\n }\n}"
- }
- }
- ]
-}