Skip to content

Commit ada565f

Browse files
committed
MODFQMMGR-746 Add LocalClient
1 parent adace33 commit ada565f

14 files changed

Lines changed: 173 additions & 216 deletions
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.folio.fqm.client;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import feign.FeignException;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.log4j.Log4j2;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.cloud.openfeign.FeignClient;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
13+
import java.time.DateTimeException;
14+
import java.time.ZoneId;
15+
16+
/**
17+
* Client for the /locale API.
18+
*
19+
* @implNote This is a separate class from the internal LocaleClientRaw interface because Feign clients must be interfaces, disallowing any injection.
20+
*/
21+
@Log4j2
22+
@Component
23+
@RequiredArgsConstructor(onConstructor_ = @Autowired)
24+
public class LocaleClient {
25+
26+
private final ObjectMapper objectMapper;
27+
private final LocaleClientRaw underlyingClient;
28+
29+
/**
30+
* Provides raw access to the /locale API.
31+
*/
32+
@FeignClient(name = "locale")
33+
interface LocaleClientRaw {
34+
@GetMapping("/locale")
35+
String getLocaleSettings();
36+
}
37+
38+
public record LocaleSettings(
39+
String locale,
40+
String currency,
41+
String timezone,
42+
String numberingSystem
43+
) {
44+
public ZoneId getZoneId() {
45+
try {
46+
return ZoneId.of(timezone);
47+
} catch (DateTimeException e) {
48+
log.error("Invalid timezone '{}', defaulting to UTC.", timezone, e);
49+
return ZoneId.of("UTC");
50+
}
51+
}
52+
}
53+
54+
public LocaleSettings getLocaleSettings() {
55+
try {
56+
String response = underlyingClient.getLocaleSettings();
57+
return objectMapper.readValue(response, LocaleSettings.class);
58+
} catch (JsonProcessingException | FeignException | NullPointerException e) {
59+
log.error("Failed to retrieve locale information. Defaulting to en-US, USD, UTC, latn.", e);
60+
return new LocaleSettings("en-US", "USD", "UTC", "latn");
61+
}
62+
}
63+
}

src/main/java/org/folio/fqm/client/SettingsClient.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/main/java/org/folio/fqm/client/SettingsClientRaw.java

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/main/java/org/folio/fqm/migration/MigrationStrategyRepository.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import org.folio.fqm.client.ModesOfIssuanceClient;
77
import org.folio.fqm.client.OrganizationsClient;
88
import org.folio.fqm.client.PatronGroupsClient;
9-
import org.folio.fqm.client.SettingsClient;
9+
import org.folio.fqm.client.LocaleClient;
1010
import org.folio.fqm.migration.strategies.MigrationStrategy;
1111
import org.folio.fqm.migration.strategies.impl.V0POCMigration;
1212
import org.folio.fqm.migration.strategies.impl.V10OrganizationStatusValueChange;
@@ -42,7 +42,7 @@ public class MigrationStrategyRepository {
4242
private final List<MigrationStrategy> migrationStrategies;
4343

4444
public MigrationStrategyRepository(
45-
SettingsClient settingsClient,
45+
LocaleClient localeClient,
4646
LocationsClient locationsClient,
4747
LocationUnitsClient locationUnitsClient,
4848
ModesOfIssuanceClient modesOfIssuanceClient,
@@ -57,7 +57,7 @@ public MigrationStrategyRepository(
5757
new V1ModeOfIssuanceConsolidation(),
5858
new V2ResourceTypeConsolidation(),
5959
new V3RamsonsFieldCleanup(),
60-
new V4DateFieldTimezoneAddition(settingsClient),
60+
new V4DateFieldTimezoneAddition(localeClient),
6161
new V5UUIDNotEqualOperatorRemoval(),
6262
new V6ModeOfIssuanceValueChange(modesOfIssuanceClient),
6363
new V7PatronGroupsValueChange(patronGroupsClient),

src/main/java/org/folio/fqm/migration/strategies/impl/V4DateFieldTimezoneAddition.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.function.Supplier;
99
import lombok.RequiredArgsConstructor;
1010
import lombok.extern.log4j.Log4j2;
11-
import org.folio.fqm.client.SettingsClient;
11+
import org.folio.fqm.client.LocaleClient;
1212
import org.folio.fqm.migration.MigrationUtils;
1313
import org.folio.fqm.migration.strategies.AbstractRegularMigrationStrategy;
1414
import org.folio.fqm.migration.types.MigratableFqlFieldAndCondition;
@@ -86,7 +86,7 @@ public String getMaximumApplicableVersion() {
8686
"users.user_updated_date"
8787
);
8888

89-
private final SettingsClient settingsClient;
89+
private final LocaleClient localeClient;
9090

9191
@Override
9292
public String getLabel() {
@@ -113,7 +113,7 @@ public SingleFieldMigrationResult<MigratableFqlFieldAndCondition> migrateFql(
113113
}
114114

115115
if (state.get() == null) {
116-
state.set(settingsClient.getTenantTimezone());
116+
state.set(localeClient.getLocaleSettings().getZoneId());
117117
}
118118

119119
try {

src/main/java/org/folio/fqm/repository/DataRefreshRepository.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package org.folio.fqm.repository;
22

3-
import com.fasterxml.jackson.databind.JsonNode;
4-
import com.fasterxml.jackson.databind.ObjectMapper;
53
import com.jayway.jsonpath.DocumentContext;
64
import com.jayway.jsonpath.JsonPath;
75
import lombok.RequiredArgsConstructor;
86
import lombok.extern.log4j.Log4j2;
7+
import org.folio.fqm.client.LocaleClient;
98
import org.folio.fqm.client.SimpleHttpClient;
109
import org.folio.fqm.service.SourceViewService;
1110
import org.jooq.DSLContext;
@@ -31,7 +30,6 @@ public class DataRefreshRepository {
3130
public static final Field<Double> EXCHANGE_RATE_FIELD = field("exchange_rate", Double.class);
3231
public static final String EXCHANGE_RATE_TABLE = "currency_exchange_rates";
3332
private static final String GET_EXCHANGE_RATE_PATH = "finance/exchange-rate";
34-
private static final String GET_LOCALE_SETTINGS_PATH = "locale";
3533

3634
private static final List<String> SYSTEM_SUPPORTED_CURRENCIES = List.of(
3735
"USD",
@@ -72,6 +70,7 @@ public class DataRefreshRepository {
7270
private final DSLContext jooqContext;
7371
private final SourceViewService sourceViewService;
7472
private final SimpleHttpClient simpleHttpClient;
73+
private final LocaleClient localeClient;
7574

7675
/**
7776
* Refresh the currency exchange rates for a tenant, based on the tenant's default system currency.
@@ -116,17 +115,7 @@ public boolean refreshExchangeRates(String tenantId) {
116115

117116
private String getSystemCurrencyCode() {
118117
log.info("Getting system currency");
119-
try {
120-
String localeSettingsResponse = simpleHttpClient.get(GET_LOCALE_SETTINGS_PATH, Map.of());
121-
ObjectMapper objectMapper = new ObjectMapper();
122-
JsonNode localeSettingsNode = objectMapper.readTree(localeSettingsResponse);
123-
return localeSettingsNode
124-
.path("currency")
125-
.asText();
126-
} catch (Exception e) {
127-
log.info("No system currency defined, defaulting to USD");
128-
return "USD";
129-
}
118+
return localeClient.getLocaleSettings().currency();
130119
}
131120

132121
private Double getExchangeRate(String fromCurrency, String toCurrency) {

src/main/java/org/folio/fqm/service/EntityTypeService.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.folio.fql.service.FqlValidationService;
1818
import org.folio.fqm.client.CrossTenantHttpClient;
1919
import org.folio.fqm.client.LanguageClient;
20-
import org.folio.fqm.client.SimpleHttpClient;
20+
import org.folio.fqm.client.LocaleClient;
2121
import org.folio.fqm.domain.dto.EntityTypeSummary;
2222
import org.folio.fqm.exception.EntityTypeInUseException;
2323
import org.folio.fqm.exception.EntityTypeNotFoundException;
@@ -78,7 +78,6 @@ public class EntityTypeService {
7878

7979
private static final int COLUMN_VALUE_DEFAULT_PAGE_SIZE = 1000;
8080
private static final String LANGUAGES_FILEPATH = "languages.json5";
81-
private static final String GET_LOCALE_SETTINGS_PATH = "locale";
8281
private static final List<String> EXCLUDED_CURRENCY_CODES = List.of(
8382
"XUA", "AYM", "AFA", "ADP", "ATS", "AZM", "BYB", "BYR", "BEF", "BOV", "BGL", "CLF", "COU", "CUC", "CYP", "NLG", "EEK", "XBA", "XBB",
8483
"XBC", "XBD", "FIM", "FRF", "XFO", "XFU", "GHC", "DEM", "XAU", "GRD", "GWP", "IEP", "ITL", "LVL", "LTL", "LUF", "MGF", "MTL", "MRO", "MXV",
@@ -99,7 +98,7 @@ public class EntityTypeService {
9998
private final LanguageClient languageClient;
10099
private final FolioExecutionContext folioExecutionContext;
101100
private final ClockService clockService;
102-
private final SimpleHttpClient simpleHttpClient;
101+
private final LocaleClient localeClient;
103102
private final TranslationService translationService;
104103

105104
/**
@@ -392,10 +391,7 @@ private ColumnValues getLanguages(String searchText, List<String> tenantsToQuery
392391

393392
Locale folioLocale;
394393
try {
395-
String localeSettingsResponse = simpleHttpClient.get(GET_LOCALE_SETTINGS_PATH, Map.of());
396-
ObjectMapper objectMapper = new ObjectMapper();
397-
JsonNode localeSettingsNode = objectMapper.readTree(localeSettingsResponse);
398-
String localeString = localeSettingsNode.path("locale").asText();
394+
String localeString = localeClient.getLocaleSettings().locale();
399395
folioLocale = new Locale(localeString.substring(0, 2)); // Java locales are in form xx, FOLIO stores locales as xx-YY
400396
} catch (Exception e) {
401397
log.debug("No default locale defined. Defaulting to English for language translations.");

src/main/java/org/folio/fqm/service/ResultSetService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import lombok.extern.log4j.Log4j2;
77
import org.apache.commons.collections4.CollectionUtils;
88
import org.apache.commons.lang3.StringUtils;
9-
import org.folio.fqm.client.SettingsClient;
9+
import org.folio.fqm.client.LocaleClient;
1010
import org.folio.fqm.repository.ResultSetRepository;
1111
import org.folio.fqm.utils.EntityTypeUtils;
1212
import org.folio.querytool.domain.dto.EntityType;
@@ -49,7 +49,7 @@ public class ResultSetService {
4949

5050
private final ResultSetRepository resultSetRepository;
5151
private final EntityTypeFlatteningService entityTypeFlatteningService;
52-
private final SettingsClient settingsClient;
52+
private final LocaleClient localeClient;
5353
private final FolioExecutionContext executionContext;
5454
private final TranslationService translationService;
5555

@@ -75,7 +75,7 @@ private List<Map<String, Object>> getSortedContents(UUID entityTypeId, List<List
7575
);
7676

7777
List<String> dateFields = localize ? EntityTypeUtils.getDateTimeFields(entityType) : List.of();
78-
ZoneId tenantTimezone = localize ? settingsClient.getTenantTimezone() : null;
78+
ZoneId tenantTimezone = localize ? localeClient.getLocaleSettings().getZoneId() : null;
7979
List<String> countryFields = EntityTypeUtils.getCountryLocalizationFieldPaths(entityType);
8080

8181
return contentIds
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.folio.fqm.client;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.is;
5+
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.when;
7+
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import feign.FeignException;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.ExtendWith;
12+
import org.mockito.InjectMocks;
13+
import org.mockito.Mock;
14+
import org.mockito.Spy;
15+
import org.mockito.junit.jupiter.MockitoExtension;
16+
17+
@ExtendWith(MockitoExtension.class)
18+
class LocaleClientTest {
19+
20+
private static final String LOCALE_JSON =
21+
"""
22+
{
23+
"locale": "en-US",
24+
"currency": "USD",
25+
"timezone": "America/New_York",
26+
"numberingSystem": "latn"
27+
}
28+
""";
29+
30+
@InjectMocks
31+
private LocaleClient localeClient;
32+
33+
@Mock
34+
private LocaleClient.LocaleClientRaw underlyingClient;
35+
36+
@Spy
37+
private ObjectMapper objectMapper = new ObjectMapper();
38+
39+
@Test
40+
void testGetLocaleSettingsWhenPresent() {
41+
when(underlyingClient.getLocaleSettings()).thenReturn(LOCALE_JSON);
42+
43+
LocaleClient.LocaleSettings expected = new LocaleClient.LocaleSettings("en-US", "USD", "America/New_York", "latn");
44+
assertThat(localeClient.getLocaleSettings(), is(expected));
45+
}
46+
47+
@Test
48+
void testGetLocaleSettingsHandlesException() {
49+
when(underlyingClient.getLocaleSettings())
50+
.thenThrow(new FeignException.Unauthorized("", mock(feign.Request.class), null, null));
51+
52+
LocaleClient.LocaleSettings expectedDefault = new LocaleClient.LocaleSettings("en-US", "USD", "UTC", "latn");
53+
assertThat(localeClient.getLocaleSettings(), is(expectedDefault));
54+
}
55+
56+
@Test
57+
void testGetLocaleSettingsHandlesInvalidJson() {
58+
when(underlyingClient.getLocaleSettings()).thenReturn("invalid json");
59+
60+
LocaleClient.LocaleSettings expectedDefault = new LocaleClient.LocaleSettings("en-US", "USD", "UTC", "latn");
61+
assertThat(localeClient.getLocaleSettings(), is(expectedDefault));
62+
}
63+
}

0 commit comments

Comments
 (0)