Skip to content

Commit

Permalink
CIRC-2171: Fetch TLR settings from mod-config as fallback (#1511)
Browse files Browse the repository at this point in the history
* CIRC-2171 Fetch TLR settings from mod-config as fallback

* CIRC-2171 Add missing permissions

* CIRC-2171 Add permissions
  • Loading branch information
OleksandrVidinieiev authored Nov 12, 2024
1 parent 0967ca8 commit e30e7ea
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 1 deletion.
4 changes: 4 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -2276,6 +2276,8 @@
"usergroups.collection.get",
"usergroups.item.get",
"pubsub.publish.post",
"configuration.entries.collection.get",
"configuration.entries.item.get",
"mod-settings.entries.item.get",
"mod-settings.entries.collection.get",
"mod-settings.global.read.circulation"
Expand Down Expand Up @@ -2375,6 +2377,8 @@
"pubsub.publish.post",
"patron-notice.post",
"circulation-storage.loans-history.collection.get",
"configuration.entries.collection.get",
"configuration.entries.item.get",
"mod-settings.entries.item.get",
"mod-settings.entries.collection.get",
"mod-settings.global.read.circulation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@

import io.vertx.core.json.JsonObject;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;

@AllArgsConstructor
@Getter
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode
public class TlrSettingsConfiguration {
protected static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
import org.folio.circulation.domain.ConfigurationService;
import org.folio.circulation.domain.MultipleRecords;
import org.folio.circulation.domain.anonymization.config.LoanAnonymizationConfiguration;
import org.folio.circulation.domain.configuration.TlrSettingsConfiguration;
import org.folio.circulation.support.Clients;
import org.folio.circulation.support.GetManyRecordsClient;
import org.folio.circulation.support.http.client.CqlQuery;
import org.folio.circulation.support.http.client.PageLimit;
import org.folio.circulation.support.results.Result;

import io.vertx.core.json.JsonObject;
import lombok.extern.log4j.Log4j2;

@Log4j2
public class ConfigurationRepository {
private static final String CONFIGS_KEY = "configs";
private static final String MODULE_NAME_KEY = "module";
Expand Down Expand Up @@ -48,6 +51,14 @@ public CompletableFuture<Result<Integer>> lookupSessionTimeout() {
return lookupConfigurations(otherSettingsQuery, applySessionTimeout());
}

public CompletableFuture<Result<TlrSettingsConfiguration>> lookupTlrSettings() {
log.info("lookupTlrSettings:: fetching TLR configuration");
Result<CqlQuery> queryResult = defineModuleNameAndConfigNameFilter(
"SETTINGS", "TLR");

return findAndMapFirstConfiguration(queryResult, TlrSettingsConfiguration::from);
}

/**
* Gets loan history tenant configuration - settings for loan anonymization
*
Expand Down Expand Up @@ -115,4 +126,26 @@ private Function<MultipleRecords<Configuration>, Integer> applySessionTimeout()
.findSessionTimeout(configurations.getRecords());
}

/**
* Find first configuration and maps it to an object with a provided mapper
*/
private <T> CompletableFuture<Result<T>> findAndMapFirstConfiguration(
Result<CqlQuery> cqlQueryResult, Function<JsonObject, T> mapper) {

return cqlQueryResult
.after(query -> configurationClient.getMany(query, DEFAULT_PAGE_LIMIT))
.thenApply(result -> result.next(r -> from(r, Configuration::new, CONFIGS_KEY)))
.thenApply(result -> result.map(this::findFirstConfigurationAsJsonObject))
.thenApply(result -> result.map(mapper));
}

private JsonObject findFirstConfigurationAsJsonObject(
MultipleRecords<Configuration> configurations) {

return configurations.getRecords().stream()
.findFirst()
.map(Configuration::getValue)
.map(JsonObject::new)
.orElse(new JsonObject());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@
import static java.util.function.Function.identity;
import static org.folio.circulation.support.http.client.CqlQuery.exactMatch;
import static org.folio.circulation.support.http.client.CqlQuery.exactMatchAny;
import static org.folio.circulation.support.results.Result.ofAsync;
import static org.folio.circulation.support.results.Result.succeeded;

public class SettingsRepository {
private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass());
private final GetManyRecordsClient settingsClient;
private final ConfigurationRepository configurationRepository;

public SettingsRepository(Clients clients) {
settingsClient = clients.settingsStorageClient();
configurationRepository = new ConfigurationRepository(clients);
}

public CompletableFuture<Result<CheckoutLockConfiguration>> lookUpCheckOutLockSettings() {
Expand All @@ -52,9 +55,10 @@ public CompletableFuture<Result<CheckoutLockConfiguration>> lookUpCheckOutLockSe
}

public CompletableFuture<Result<TlrSettingsConfiguration>> lookupTlrSettings() {
log.info("lookupTlrSettings:: fetching TLR settings");
return fetchSettings("circulation", List.of("generalTlr", "regularTlr"))
.thenApply(r -> r.map(SettingsRepository::extractAndMergeValues))
.thenApply(r -> r.map(TlrSettingsConfiguration::from));
.thenCompose(r -> r.after(this::buildTlrSettings));
}

private CompletableFuture<Result<MultipleRecords<JsonObject>>> fetchSettings(String scope, String key) {
Expand All @@ -76,4 +80,13 @@ private static JsonObject extractAndMergeValues(MultipleRecords<JsonObject> entr
.map(rec -> rec.getJsonObject("value"))
.reduce(new JsonObject(), JsonObject::mergeIn);
}

private CompletableFuture<Result<TlrSettingsConfiguration>> buildTlrSettings(JsonObject tlrSettings) {
if (tlrSettings.isEmpty()) {
log.info("getTlrSettings:: failed to find TLR settings, falling back to legacy configuration");
return configurationRepository.lookupTlrSettings();
}

return ofAsync(TlrSettingsConfiguration.from(tlrSettings));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import lombok.SneakyThrows;

import org.folio.circulation.domain.configuration.TlrSettingsConfiguration;
import org.folio.circulation.support.Clients;
import org.folio.circulation.support.CollectionResourceClient;
import org.folio.circulation.support.ServerErrorFailure;
Expand All @@ -13,11 +16,16 @@
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import static org.folio.circulation.support.results.Result.ofAsync;
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.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

class SettingsRepositoryTest {
Expand Down Expand Up @@ -58,6 +66,93 @@ void testFetchSettingsWhenSettingsApiThrowError() throws ExecutionException, Int
assertFalse(res.isCheckOutLockFeatureEnabled());
}

@Test
@SneakyThrows
void fetchTlrSettings() {
Clients clients = mock(Clients.class);
CollectionResourceClient settingsClient = mock(CollectionResourceClient.class);
CollectionResourceClient configurationClient = mock(CollectionResourceClient.class);

JsonObject mockSettingsResponse = new JsonObject()
.put("items", new JsonArray()
.add(new JsonObject()
.put("id", UUID.randomUUID().toString())
.put("scope", "circulation")
.put("key", "generalTlr")
.put("value", new JsonObject()
.put("titleLevelRequestsFeatureEnabled", true)
.put("createTitleLevelRequestsByDefault", true)
.put("tlrHoldShouldFollowCirculationRules", true))))
.put("resultInfo", new JsonObject()
.put("totalRecords", 0)
.put("diagnostics", new JsonArray()));

when(clients.settingsStorageClient()).thenReturn(settingsClient);
when(clients.configurationStorageClient()).thenReturn(configurationClient);
when(settingsClient.getMany(any(), any()))
.thenReturn(ofAsync(new Response(200, mockSettingsResponse.encode(), "application/json")));

TlrSettingsConfiguration actualResult = new SettingsRepository(clients)
.lookupTlrSettings()
.get(30, TimeUnit.SECONDS)
.value();

assertEquals(new TlrSettingsConfiguration(true, true, true, null, null, null), actualResult);
verify(settingsClient).getMany(any(), any());
verifyNoInteractions(configurationClient);
}

@Test
@SneakyThrows
void fallBackToLegacyConfigurationWhenTlrSettingsAreNotFound() {
Clients clients = mock(Clients.class);
CollectionResourceClient settingsClient = mock(CollectionResourceClient.class);
CollectionResourceClient configurationClient = mock(CollectionResourceClient.class);

JsonObject mockEmptySettingsResponse = new JsonObject()
.put("items", new JsonArray())
.put("resultInfo", new JsonObject()
.put("totalRecords", 0)
.put("diagnostics", new JsonArray()));

JsonObject mockConfigurationResponse = new JsonObject()
.put("configs", new JsonArray().add(
new JsonObject()
.put("id", UUID.randomUUID().toString())
.put("module", "SETTINGS")
.put("configName", "TLR")
.put("enabled", true)
.put("value", new JsonObject()
.put("titleLevelRequestsFeatureEnabled", true)
.put("createTitleLevelRequestsByDefault", true)
.put("tlrHoldShouldFollowCirculationRules", true)
.put("confirmationPatronNoticeTemplateId", null)
.put("cancellationPatronNoticeTemplateId", null)
.put("expirationPatronNoticeTemplateId", null)
.encode())))
.put("totalRecords", 1)
.put("resultInfo", new JsonObject()
.put("totalRecords", 1)
.put("facets", new JsonArray())
.put("diagnostics", new JsonArray()));

when(clients.settingsStorageClient()).thenReturn(settingsClient);
when(clients.configurationStorageClient()).thenReturn(configurationClient);
when(settingsClient.getMany(any(), any()))
.thenReturn(ofAsync(new Response(200, mockEmptySettingsResponse.encode(), "application/json")));
when(configurationClient.getMany(any(), any()))
.thenReturn(ofAsync(new Response(200, mockConfigurationResponse.encode(), "application/json")));

TlrSettingsConfiguration actualResult = new SettingsRepository(clients)
.lookupTlrSettings()
.get(30, TimeUnit.SECONDS)
.value();

assertEquals(new TlrSettingsConfiguration(true, true, true, null, null, null), actualResult);
verify(settingsClient).getMany(any(), any());
verify(configurationClient).getMany(any(), any());
}

private JsonObject createCheckoutLockJsonResponse(boolean checkoutFeatureFlag) {
JsonObject checkoutLockResponseJson = new JsonObject();
checkoutLockResponseJson.put("id", UUID.randomUUID())
Expand Down

0 comments on commit e30e7ea

Please sign in to comment.