diff --git a/NEWS.md b/NEWS.md index e24f7d1..d7a04b2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,6 @@ +## 2025-XX-XX v1.23.0-SNAPSHOT +* Replace `mod-configuration` with `mod-settings` to get language and locale settings ([MODTEMPENG-117](https://folio-org.atlassian.net/browse/MODTEMPENG-117)) + ## 2025-03-13 v1.22.0 * Support barcode image generate for HRID tokens (MODTEMPENG-111) * Upgrade Java and RMB version for Sunflower (FOLIO-4220) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 2789bbe..46bef43 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -2,15 +2,14 @@ "id": "${artifactId}-${version}", "name": "Template engine module", "requires": [ - { - "id": "configuration", - "version": "2.0" - }, { "id": "patron-notice-policy-storage", "version": "0.13" + }, + { + "id": "settings", + "version": "1.2" } - ], "provides": [ { @@ -49,7 +48,7 @@ "pathPattern": "/template-request", "permissionsRequired": ["template-request.post"], "modulePermissions": [ - "configuration.entries.collection.get" + "mod-settings.global.read.stripes-core.prefs.manage" ] } ] diff --git a/ramls/configuration.json b/ramls/configuration.json deleted file mode 100644 index f408586..0000000 --- a/ramls/configuration.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "description": "Configuration", - "properties": { - "id": { - "type": "string", - "description": "Configuration id", - "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}$" - }, - "module": { - "type": "string", - "description": "Module name" - }, - "configName": { - "type": "string", - "description": "Config name" - }, - "code": { - "type": "string", - "description": "Configuration code" - }, - "description": { - "type": "string", - "description": "Configuration record description" - }, - "default": { - "type": "boolean", - "description": "Value is default" - }, - "enabled": { - "type": "boolean", - "description": "Configuration is enabled" - }, - "value": { - "type": "string", - "description": "Configuration value" - }, - "userId": { - "type": "string", - "description": "User id" - }, - "metadata": { - "$ref": "raml-util/schemas/metadata.schema", - "readonly": true - } - }, - "additionalProperties": false, - "required": [ - "module", - "configName" - ] -} diff --git a/ramls/configurations.json b/ramls/configurations.json deleted file mode 100644 index f13dc12..0000000 --- a/ramls/configurations.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "description": "Collection of configuration records", - "properties": { - "configs": { - "id": "configurationData", - "description": "Array of configuration records", - "type": "array", - "items": { - "type": "object", - "$ref": "configuration.json" - } - }, - "totalRecords": { - "type": "integer", - "description": "Total number of records" - }, - "resultInfo": { - "$ref": "raml-util/schemas/resultInfo.schema", - "readonly": true - } - }, - "additionalProperties": false, - "required": [ - "configs", - "totalRecords" - ] -} diff --git a/ramls/template-engine.raml b/ramls/template-engine.raml index 5e5fa25..1e5081c 100644 --- a/ramls/template-engine.raml +++ b/ramls/template-engine.raml @@ -15,8 +15,6 @@ types: templateProcessingRequest: !include templateProcessingRequest.json templateProcessingResult: !include templateProcessingResult.json errors: !include raml-util/schemas/errors.schema - configuration: !include configuration.json - configurations: !include configurations.json traits: pageable: !include ./raml-util/traits/pageable.raml diff --git a/src/main/java/org/folio/template/client/ConfigurationClient.java b/src/main/java/org/folio/template/client/ConfigurationClient.java deleted file mode 100644 index 349259e..0000000 --- a/src/main/java/org/folio/template/client/ConfigurationClient.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.folio.template.client; - - -import static java.lang.String.format; - -import java.util.Collection; -import java.util.Map; -import java.util.Optional; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.folio.HttpStatus; -import org.folio.rest.jaxrs.model.Config; -import org.folio.rest.jaxrs.model.Configurations; -import org.folio.template.util.OkapiModuleClientException; - -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.json.JsonObject; - -public class ConfigurationClient extends OkapiClient { - private static final Logger LOG = LogManager.getLogger("mod-template-engine"); - - private static final String DEFAULT_LANGUAGE_TAG = "en-US"; - private static final String DEFAULT_TIMEZONE_ID = "UTC"; - private String configRequestPath; - - public ConfigurationClient(Vertx vertx, Map okapiHeaders) { - super(vertx, okapiHeaders); - this.configRequestPath = System.getProperty("config.client.path", "/configurations/entries"); - } - - public Future lookupLocaleConfig() { - LOG.debug("lookupLocaleConfig:: Lookup locale configuration"); - LOG.info("lookupLocaleConfig:: Locale configuration looked up successfully"); - return lookupConfigByModuleAndConfigName("ORG", "localeSettings", 1, 0) - .map(this::mapToLocaleConfiguration); - } - - private Future lookupConfigByModuleAndConfigName(String moduleName, - String configName, int limit, int offset) { - - LOG.debug("lookupConfigByModuleAndConfigName:: Lookup locale configuration by Module Name {} and Config Name {}",moduleName,configName); - String query = format("module=%s and configName=%s", moduleName, configName); - LOG.info("lookupConfigByModuleAndConfigName:: Locale configuration with module and config name looked up successfully"); - return lookupConfigByQuery(query, limit, offset); - } - - private Future lookupConfigByQuery(String query, int limit, int offset) { - LOG.debug("lookupConfigByQuery:: Lookup configuration by Query {}",query); - return getMany(configRequestPath, query, limit, offset).future() - .map(response -> { - if (response.statusCode() != HttpStatus.HTTP_OK.toInt()) { - LOG.warn("Error getting config by module name. Status: {}, body: {}", response.statusCode(), response.body()); - throw new OkapiModuleClientException(format("Error getting config by module name. " + - "Status: %d, body: %s", response.statusCode(), response.body())); - } - LOG.info("lookupConfigByQuery:: Locale configuration by query looked up successfully"); - return response.bodyAsJsonObject().mapTo(Configurations.class); - }); - } - - private LocaleConfiguration mapToLocaleConfiguration(Configurations configurations) { - LOG.debug("mapToLocaleConfiguration:: Mapping configurations {} to locale configuration", configurations); - JsonObject localeConfig = Optional.ofNullable(configurations.getConfigs()).stream() - .flatMap(Collection::stream) - .findFirst() - .map(Config::getValue) - .map(JsonObject::new) - .orElse(new JsonObject()); - - String languageTag = localeConfig.getString("locale", DEFAULT_LANGUAGE_TAG); - String timezoneId = localeConfig.getString("timezone", DEFAULT_TIMEZONE_ID); - LOG.info("mapToLocaleConfiguration:: Mapped to locale configuration with Language Tag: {}, Timezone ID: {}", languageTag, timezoneId); - return new LocaleConfiguration(languageTag, timezoneId); - } -} diff --git a/src/main/java/org/folio/template/client/LocaleConfiguration.java b/src/main/java/org/folio/template/client/LocaleSettings.java similarity index 74% rename from src/main/java/org/folio/template/client/LocaleConfiguration.java rename to src/main/java/org/folio/template/client/LocaleSettings.java index b548c08..01b1f35 100644 --- a/src/main/java/org/folio/template/client/LocaleConfiguration.java +++ b/src/main/java/org/folio/template/client/LocaleSettings.java @@ -1,11 +1,11 @@ package org.folio.template.client; -public class LocaleConfiguration { +public class LocaleSettings { private final String languageTag; private final String timeZoneId; - public LocaleConfiguration(String languageTag, String timeZoneId) { + public LocaleSettings(String languageTag, String timeZoneId) { this.languageTag = languageTag; this.timeZoneId = timeZoneId; } diff --git a/src/main/java/org/folio/template/client/SettingsClient.java b/src/main/java/org/folio/template/client/SettingsClient.java new file mode 100644 index 0000000..308b2d3 --- /dev/null +++ b/src/main/java/org/folio/template/client/SettingsClient.java @@ -0,0 +1,81 @@ +package org.folio.template.client; + +import static java.lang.String.format; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.HttpStatus; +import org.folio.template.util.OkapiModuleClientException; + +public class SettingsClient extends OkapiClient { + private static final Logger LOG = LogManager.getLogger("mod-template-engine"); + + private static final String DEFAULT_LANGUAGE_TAG = "en-US"; + private static final String DEFAULT_TIMEZONE_ID = "UTC"; + private static final String LOCALE_SETTINGS_SCOPE = "stripes-core.prefs.manage"; + private static final String LOCALE_SETTINGS_KEY = "tenantLocaleSettings"; + private final String settingsRequestPath; + + public SettingsClient(Vertx vertx, Map okapiHeaders) { + super(vertx, okapiHeaders); + this.settingsRequestPath = System.getProperty("config.client.path", "/settings/entries"); + } + + public Future lookupLocaleSetting() { + LOG.debug("lookupLocaleConfig:: Lookup locale setting"); + var configs = lookupSettingsByScopeAndKey(LOCALE_SETTINGS_SCOPE, LOCALE_SETTINGS_KEY, 1, 0) + .map(this::mapToLocaleSettings); + LOG.info("lookupLocaleConfig:: Locale setting looked up successfully"); + + return configs; + } + + private Future lookupSettingsByScopeAndKey(String scope, String key, int limit, int offset) { + + LOG.debug("lookupSettingsByScopeAndKey:: Lookup locale settings by scope {} and key {}", scope, key); + String query = format("scope==%s and key==%s", scope, key); + return lookupSettingsByQuery(query, limit, offset); + } + + private Future lookupSettingsByQuery(String query, int limit, int offset) { + LOG.debug("lookupSettingsByQuery:: Lookup settings by Query {}", query); + return getMany(settingsRequestPath, query, limit, offset).future() + .map(response -> { + if (response.statusCode() != HttpStatus.HTTP_OK.toInt()) { + LOG.warn("lookupSettingsByQuery:: Error getting locale settings. Status: {}, body: {}", + response.statusCode(), response.body()); + throw new OkapiModuleClientException( + format("Error getting locale settings. Status: %d, body: %s", response.statusCode(), response.body())); + } + LOG.info("lookupSettingsByQuery:: Locale settings by query looked up successfully."); + return response.bodyAsJsonObject(); + }); + } + + private LocaleSettings mapToLocaleSettings(JsonObject localeSettings) { + LOG.debug("mapToLocaleSettings:: Mapping {} to locale setting", localeSettings); + JsonObject localeSetting = Optional.ofNullable(localeSettings.getJsonArray("items")) + .map(JsonArray::stream) + .orElse(Stream.empty()) + .filter(Objects::nonNull) + .map(JsonObject.class::cast) + .findFirst() + .orElse(new JsonObject()); + LOG.debug("mapToLocaleSettings:: Found locale setting: {}", localeSetting); + + var valueObj = Optional.ofNullable(localeSetting.getJsonObject("value")).orElse(new JsonObject()); + String languageTag = valueObj.getString("locale", DEFAULT_LANGUAGE_TAG); + String timezoneId = valueObj.getString("timezone", DEFAULT_TIMEZONE_ID); + LOG.info("mapToLocaleSettings:: Mapped to locale setting with Language Tag: {}, Timezone ID: {}", + languageTag, timezoneId); + return new LocaleSettings(languageTag, timezoneId); + } +} diff --git a/src/main/java/org/folio/template/service/TemplateServiceImpl.java b/src/main/java/org/folio/template/service/TemplateServiceImpl.java index 2b66694..33f2e20 100644 --- a/src/main/java/org/folio/template/service/TemplateServiceImpl.java +++ b/src/main/java/org/folio/template/service/TemplateServiceImpl.java @@ -24,8 +24,8 @@ import org.folio.rest.jaxrs.model.TemplateProcessingResult; import org.folio.template.InUseTemplateException; import org.folio.template.client.CirculationStorageClient; -import org.folio.template.client.ConfigurationClient; -import org.folio.template.client.LocaleConfiguration; +import org.folio.template.client.LocaleSettings; +import org.folio.template.client.SettingsClient; import org.folio.template.dao.TemplateDao; import org.folio.template.dao.TemplateDaoImpl; import org.folio.template.resolver.TemplateResolver; @@ -44,7 +44,7 @@ public class TemplateServiceImpl implements TemplateService { private Vertx vertx; private TemplateDao templateDao; private Map templateResolverAddressesMap; - private ConfigurationClient configurationClient; + private SettingsClient settingsClient; private CirculationStorageClient circulationStorageClient; @@ -53,7 +53,7 @@ public TemplateServiceImpl(Vertx vertx, Map okapiHeaders) { this.templateDao = new TemplateDaoImpl(vertx, okapiHeaders.get(TENANT)); this.templateResolverAddressesMap = vertx.sharedData().getLocalMap( TemplateEngineHelper.TEMPLATE_RESOLVERS_LOCAL_MAP); - this.configurationClient = new ConfigurationClient(vertx, okapiHeaders); + this.settingsClient = new SettingsClient(vertx, okapiHeaders); this.circulationStorageClient = new CirculationStorageClient(vertx, okapiHeaders); } @@ -109,7 +109,7 @@ public Future processTemplate(TemplateProcessingReques .map(optionalTemplate -> optionalTemplate.orElseThrow(() -> new BadRequestException(String.format("Template with id %s does not exist", templateRequest.getTemplateId())))); - Future localeConfigurationFuture = configurationClient.lookupLocaleConfig(); + Future localeConfigurationFuture = settingsClient.lookupLocaleSetting(); return CompositeFuture.all(templateByIdFuture, localeConfigurationFuture) .compose(compositeFuture -> { @@ -123,7 +123,7 @@ public Future processTemplate(TemplateProcessingReques .map(JsonObject::mapFrom) .orElse(new JsonObject()); - LocaleConfiguration config = compositeFuture.resultAt(1); + LocaleSettings config = compositeFuture.resultAt(1); TemplateContextPreProcessor preProcessor = new TemplateContextPreProcessor(templateContent, contextObject, config); preProcessor.process(); @@ -146,7 +146,9 @@ public Future processTemplate(TemplateProcessingReques .withDateCreate(new Date()) .withLang(templateRequest.getLang()) .withOutputFormat(templateRequest.getOutputFormat()); + LOG.info("processTemplate:: Template processed successfully"); + return new TemplateProcessingResult() .withResult(processedTemplate) .withMeta(resultMetaInfo) diff --git a/src/main/java/org/folio/template/util/TemplateContextPreProcessor.java b/src/main/java/org/folio/template/util/TemplateContextPreProcessor.java index 06cab37..9c6f8a1 100644 --- a/src/main/java/org/folio/template/util/TemplateContextPreProcessor.java +++ b/src/main/java/org/folio/template/util/TemplateContextPreProcessor.java @@ -8,7 +8,7 @@ import org.folio.rest.jaxrs.model.Attachment; import org.folio.rest.jaxrs.model.LocalizedTemplatesProperty; import org.folio.rest.tools.parser.JsonPathParser; -import org.folio.template.client.LocaleConfiguration; +import org.folio.template.client.LocaleSettings; import java.util.*; import java.util.regex.Matcher; @@ -24,7 +24,8 @@ public class TemplateContextPreProcessor { private static final String HTML_IMG_TEMPLATE = "%s"; private static final String TOKEN_TEMPLATE_REGULAR = "{{%s}}"; private static final String TOKEN_TEMPLATE_HTML = "{{{%s}}}"; - private static final String TOKEN_PATTERN = "\\{\\{([.a-zA-Z]+)}}"; + private static final String TOKEN_REGEX = "\\{\\{([.a-zA-Z]+)}}"; + private static final Pattern TOKEN_PATTERN = Pattern.compile(TOKEN_REGEX); private static final String CONTENT_TYPE_PNG = "image/png"; private static final String SUFFIX_DATE = "Date"; @@ -34,13 +35,13 @@ public class TemplateContextPreProcessor { private final LocalizedTemplatesProperty template; private final JsonObject context; - private final LocaleConfiguration config; + private final LocaleSettings config; private final Map attachments; private final JsonPathParser jsonParser; private final Set templateTokens; public TemplateContextPreProcessor( - LocalizedTemplatesProperty template, JsonObject context, LocaleConfiguration config) { + LocalizedTemplatesProperty template, JsonObject context, LocaleSettings config) { this.template = template; this.context = context; this.config = config; @@ -115,8 +116,7 @@ private Map getContextMap() { private Set getTokensFromTemplate() { LOG.debug("getTokensFromTemplate:: Retrieving tokens from template"); Set tokens = new HashSet<>(); - Matcher matcher = Pattern.compile(TOKEN_PATTERN) - .matcher(template.getHeader() + template.getBody()); + Matcher matcher = TOKEN_PATTERN.matcher(template.getHeader() + template.getBody()); while (matcher.find()) { tokens.add(matcher.group(1)); } diff --git a/src/test/java/org/folio/rest/impl/BarcodeIT.java b/src/test/java/org/folio/rest/impl/BarcodeIT.java index c39e35d..0e71bda 100644 --- a/src/test/java/org/folio/rest/impl/BarcodeIT.java +++ b/src/test/java/org/folio/rest/impl/BarcodeIT.java @@ -58,7 +58,7 @@ class BarcodeIT { public static final GenericContainer okapi = new GenericContainer<>(DockerImageName.parse("busybox:1.35.0-uclibc")) .withCommand("busybox httpd -f -v -p 9130") - .withCopyToContainer(Transferable.of("{}"), "/configurations/entries") + .withCopyToContainer(Transferable.of("{}"), "/settings/entries") .withNetwork(network) .withNetworkAliases("okapi") .withExposedPorts(9130); diff --git a/src/test/java/org/folio/rest/impl/TemplateRequestTest.java b/src/test/java/org/folio/rest/impl/TemplateRequestTest.java index 12198e5..8e15df3 100644 --- a/src/test/java/org/folio/rest/impl/TemplateRequestTest.java +++ b/src/test/java/org/folio/rest/impl/TemplateRequestTest.java @@ -53,7 +53,7 @@ public class TemplateRequestTest { private static final String TEMPLATE_PATH = "/templates"; private static final String TEMPLATE_REQUEST_PATH = "/template-request"; - private static final String CONFIG_REQUEST_PATH = "/configurations/entries"; + private static final String SETTINGS_REQUEST_PATH = "/settings/entries"; private static final String TXT_OUTPUT_FORMAT = "txt"; private static final String HTML_OUTPUT_FORMAT = "html"; @@ -1004,22 +1004,24 @@ private Template createTemplateWithMultipleItems() { } private void mockConfigModule() { - Configurations configurations = - new Configurations().withConfigs(Collections.emptyList()).withTotalRecords(0); - stubFor(get(urlPathEqualTo(CONFIG_REQUEST_PATH)) - .willReturn(okJson(toJson(mapFrom(configurations))))); + var settings = new JsonObject().put("items", new JsonArray()); + stubFor(get(urlPathEqualTo(SETTINGS_REQUEST_PATH)) + .willReturn(okJson(toJson(settings)))); } private void mockLocaleSettings(String languageToken, String timezoneId) { - String localeConfigValue = new JsonObject() + var localeConfigValue = new JsonObject() .put("locale", languageToken) - .put("timezone", timezoneId).encode(); + .put("timezone", timezoneId); - Config config = new Config().withValue(localeConfigValue); - Configurations configurations = - new Configurations().withConfigs(Collections.singletonList(config)).withTotalRecords(1); + var setting = new JsonObject() + .put("scope", "stripes-core.prefs.manage") + .put("key", "tenantLocaleSettings") + .put("value", localeConfigValue); + var settings = new JsonObject() + .put("items", new JsonArray().add(setting)); - stubFor(get(urlPathEqualTo(CONFIG_REQUEST_PATH)) - .willReturn(okJson(toJson(configurations)))); + stubFor(get(urlPathEqualTo(SETTINGS_REQUEST_PATH)) + .willReturn(okJson(toJson(settings)))); } }