Skip to content

Commit 5dd0370

Browse files
authored
Merge pull request #612 from ansforge/feat/dispatcher-use-env-for-config
Feat/dispatcher use env for config
2 parents 97bf0f7 + 2609026 commit 5dd0370

11 files changed

Lines changed: 202 additions & 32 deletions

File tree

hub/dispatcher/src/main/java/com/hubsante/hub/config/Constants.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,12 @@ public class Constants {
3838
public static final String FR_HEALTH_PREFIX = "fr.health";
3939
public static final String FR_FIRE_PREFIX = "fr.fire";
4040
public static final String FR_CISU_PREFIX = "fr.cisu";
41-
public static final String NEXSIS_VHOST = "15-nexsis_v1.9";
4241
public static final String HEALTH_VHOST_PREFIX = "15-15_v";
42+
public static final String NEXSIS_HUBEX_PARTNER = "fire";
4343
public static final Map<String, String> HUBEX_PERIMETER_PREFIXES =
4444
Map.of(
4545
"15-15", "fr.health",
4646
"15-nexsis", "fr.fire");
47-
public static final Map<String, String> CONVERSION_VHOST_MODEL =
48-
Map.of(
49-
"15-15_v1.5", "v1",
50-
"15-15_v2.0", "v2",
51-
"15-15_v2.1", "v3",
52-
"15-nexsis_v1.9", "v3");
5347

5448
public enum Perimeter {
5549
HEALTH("15-15"),
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Copyright © 2023-2026 Agence du Numerique en Sante (ANS)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.hubsante.hub.service;
17+
18+
import com.hubsante.hub.config.Constants;
19+
import com.hubsante.hub.exception.ClientConfigurationException;
20+
import java.util.Map;
21+
import org.springframework.beans.factory.annotation.Value;
22+
import org.springframework.beans.factory.config.YamlMapFactoryBean;
23+
import org.springframework.core.io.Resource;
24+
import org.springframework.stereotype.Component;
25+
26+
@Component
27+
public class TopologyRegistry {
28+
29+
// Spring creates exactly one instance of this bean; static utility classes that can't
30+
// take a constructor-injected dependency (MessagePersistencePolicy, ConversionUtils, ...)
31+
// read it through this self-registered reference instead of threading it through every
32+
// method signature.
33+
private static TopologyRegistry instance;
34+
35+
private final Map<String, String> majorModelVersionPerVhost;
36+
private final Map<String, String> vhostTargetPerHubexPartner;
37+
38+
@SuppressWarnings("unchecked")
39+
public TopologyRegistry(@Value("${client.configuration.file}") Resource resource) {
40+
YamlMapFactoryBean mapFactoryBean = new YamlMapFactoryBean();
41+
mapFactoryBean.setResources(resource);
42+
Map<String, Object> root = mapFactoryBean.getObject();
43+
44+
majorModelVersionPerVhost = (Map<String, String>) root.get("majorModelVersionPerVhost");
45+
vhostTargetPerHubexPartner = (Map<String, String>) root.get("vhostTargetPerHubexPartner");
46+
47+
if (majorModelVersionPerVhost == null || vhostTargetPerHubexPartner == null) {
48+
throw new ClientConfigurationException(
49+
"Missing 'majorModelVersionPerVhost' or 'vhostTargetPerHubexPartner' in topology configuration file");
50+
}
51+
if (vhostTargetPerHubexPartner.get(Constants.NEXSIS_HUBEX_PARTNER) == null) {
52+
throw new ClientConfigurationException(
53+
"Missing '"
54+
+ Constants.NEXSIS_HUBEX_PARTNER
55+
+ "' entry in 'vhostTargetPerHubexPartner' topology configuration");
56+
}
57+
instance = this;
58+
}
59+
60+
public static TopologyRegistry getInstance() {
61+
return instance;
62+
}
63+
64+
public String getMajorModelVersion(String vhost) {
65+
return majorModelVersionPerVhost.get(vhost);
66+
}
67+
68+
public String getVhostTarget(String hubexPartner) {
69+
return vhostTargetPerHubexPartner.get(hubexPartner);
70+
}
71+
}

hub/dispatcher/src/main/java/com/hubsante/hub/utils/ConversionRulesCommand.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
*/
1616
package com.hubsante.hub.utils;
1717

18-
import static com.hubsante.hub.config.Constants.CONVERSION_VHOST_MODEL;
19-
2018
import com.hubsante.hub.config.HubConfiguration;
19+
import com.hubsante.hub.service.TopologyRegistry;
2120
import com.hubsante.model.edxl.EdxlMessage;
2221

2322
public class ConversionRulesCommand {
@@ -65,10 +64,11 @@ public EdxlMessage getEdxlMessage() {
6564
}
6665

6766
public String getVHostMatchingModelVersion(String vHost) {
68-
if (CONVERSION_VHOST_MODEL.get(vHost) == null) {
67+
String modelVersion = TopologyRegistry.getInstance().getMajorModelVersion(vHost);
68+
if (modelVersion == null) {
6969
throw new IllegalArgumentException(
7070
"There is no model version associated with the host " + vHost);
7171
}
72-
return CONVERSION_VHOST_MODEL.get(vHost);
72+
return modelVersion;
7373
}
7474
}

hub/dispatcher/src/main/java/com/hubsante/hub/utils/ConversionUtils.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static com.hubsante.hub.utils.MessageUtils.*;
2121

2222
import com.hubsante.hub.config.HubConfiguration;
23+
import com.hubsante.hub.service.TopologyRegistry;
2324
import com.hubsante.model.edxl.EdxlMessage;
2425
import java.util.Arrays;
2526
import lombok.extern.slf4j.Slf4j;
@@ -54,7 +55,7 @@ public static boolean requiresVersionConversion(
5455
}
5556

5657
public static boolean isConversionAvailable(String vhost) {
57-
return CONVERSION_VHOST_MODEL.get(vhost) != null;
58+
return TopologyRegistry.getInstance().getMajorModelVersion(vhost) != null;
5859
}
5960

6061
public static String getSourceVHost(HubConfiguration hubConfig) {
@@ -72,7 +73,9 @@ public static String[] getTargetVHosts(HubConfiguration hubConfig, EdxlMessage e
7273
boolean isNexsisRecipient =
7374
recipientId.startsWith(FR_FIRE_PREFIX) || recipientId.startsWith(FR_CISU_PREFIX);
7475
if (isNexsisRecipient) {
75-
return new String[] {NEXSIS_VHOST}; // ["15-nexsis_v1.9"]
76+
return new String[] {
77+
TopologyRegistry.getInstance().getVhostTarget(NEXSIS_HUBEX_PARTNER)
78+
};
7679
}
7780
boolean isCisuSender = !senderId.startsWith(FR_HEALTH_PREFIX);
7881
boolean isDirectCisu = isDirectCisuForHealthActor(hubConfig, edxlMessage);
@@ -119,7 +122,8 @@ public static boolean isAlreadyCisuConverted(String currentVHost, String recipie
119122
if (recipient.startsWith(FR_HEALTH_PREFIX)) {
120123
return currentVHost.startsWith(HEALTH_VHOST_PREFIX);
121124
} else {
122-
return currentVHost.startsWith(NEXSIS_VHOST);
125+
return currentVHost.startsWith(
126+
TopologyRegistry.getInstance().getVhostTarget(NEXSIS_HUBEX_PARTNER));
123127
}
124128
}
125129

hub/dispatcher/src/main/java/com/hubsante/hub/utils/MessagePersistencePolicy.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
package com.hubsante.hub.utils;
1717

1818
import static com.hubsante.hub.config.Constants.HEALTH_VHOST_PREFIX;
19-
import static com.hubsante.hub.config.Constants.NEXSIS_VHOST;
19+
import static com.hubsante.hub.config.Constants.NEXSIS_HUBEX_PARTNER;
2020

21+
import com.hubsante.hub.service.TopologyRegistry;
22+
import java.util.Objects;
2123
import java.util.Set;
2224

2325
/**
@@ -46,7 +48,8 @@ private MessagePersistencePolicy() {}
4648
* Returns true if the message with the given useCase from the given vhost should be persisted.
4749
*/
4850
public static boolean shouldPersist(String vhost, String useCase) {
49-
if (NEXSIS_VHOST.equals(vhost)) {
51+
String nexsisVhost = TopologyRegistry.getInstance().getVhostTarget(NEXSIS_HUBEX_PARTNER);
52+
if (Objects.equals(nexsisVhost, vhost)) {
5053
return NEXSIS_PERSISTED_USE_CASES.contains(useCase);
5154
}
5255
if (vhost != null && vhost.startsWith(HEALTH_VHOST_PREFIX)) {

hub/dispatcher/src/main/resources/clients.template.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,11 @@ clients:
3232
- name: '15-nexsis'
3333
versions:
3434
- '1.9'
35-
editor: ANS
35+
editor: ANS
36+
majorModelVersionPerVhost:
37+
15-15_v1.5: "v1"
38+
15-15_v2.0: "v2"
39+
15-15_v2.1: "v3"
40+
15-nexsis_v1.9: "v3"
41+
vhostTargetPerHubexPartner:
42+
fire: "15-nexsis_v1.9"

hub/dispatcher/src/test/java/com/hubsante/hub/service/ConversionUtilsTest.java

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
*/
1616
package com.hubsante.hub.service;
1717

18-
import static com.hubsante.hub.utils.ConversionUtils.isAlreadyCisuConverted;
19-
import static com.hubsante.hub.utils.ConversionUtils.trimVersionSuffix;
2018
import static org.junit.jupiter.api.Assertions.*;
2119
import static org.mockito.ArgumentMatchers.anyString;
2220
import static org.mockito.Mockito.mockStatic;
@@ -29,6 +27,7 @@
2927
import java.util.Arrays;
3028
import java.util.List;
3129
import java.util.stream.Stream;
30+
import org.junit.jupiter.api.BeforeAll;
3231
import org.junit.jupiter.api.BeforeEach;
3332
import org.junit.jupiter.api.Test;
3433
import org.junit.jupiter.params.ParameterizedTest;
@@ -38,6 +37,7 @@
3837
import org.mockito.Mock;
3938
import org.mockito.MockedStatic;
4039
import org.mockito.MockitoAnnotations;
40+
import org.springframework.core.io.ClassPathResource;
4141

4242
public class ConversionUtilsTest {
4343

@@ -50,6 +50,11 @@ public class ConversionUtilsTest {
5050
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
5151
private EdxlMessage edxlMessage;
5252

53+
@BeforeAll
54+
static void setUpTopologyRegistry() {
55+
new TopologyRegistry(new ClassPathResource("config/clients.yaml"));
56+
}
57+
5358
@BeforeEach
5459
void setUp() {
5560
MockitoAnnotations.openMocks(this);
@@ -443,22 +448,24 @@ void testBuildExchange() {
443448

444449
@Test
445450
public void isAlreadyCisuConvertedTest() {
446-
assertTrue(isAlreadyCisuConverted("15-15_v1.5", "fr.health.something"));
447-
assertTrue(isAlreadyCisuConverted("15-nexsis_v1.9", "fr.fire.something-else"));
448-
449-
assertFalse(isAlreadyCisuConverted("15-15_v1.5", "fr.fire.something-else"));
450-
assertFalse(isAlreadyCisuConverted("15-nexsis_v1.9", "fr.health.something"));
451-
assertFalse(isAlreadyCisuConverted("15-smur_v1.7", "fr.health.something"));
451+
assertTrue(ConversionUtils.isAlreadyCisuConverted("15-15_v1.5", "fr.health.something"));
452+
assertTrue(
453+
ConversionUtils.isAlreadyCisuConverted("15-nexsis_v1.9", "fr.fire.something-else"));
454+
455+
assertFalse(ConversionUtils.isAlreadyCisuConverted("15-15_v1.5", "fr.fire.something-else"));
456+
assertFalse(
457+
ConversionUtils.isAlreadyCisuConverted("15-nexsis_v1.9", "fr.health.something"));
458+
assertFalse(ConversionUtils.isAlreadyCisuConverted("15-smur_v1.7", "fr.health.something"));
452459
}
453460

454461
@Test
455462
public void testTrimVersionSuffix() {
456-
assertEquals("15-15", trimVersionSuffix("15-15_v1.3"));
457-
assertEquals("15-nexsis", trimVersionSuffix("15-nexsis_v2"));
458-
assertEquals("backup", trimVersionSuffix("backup_v2.0.1"));
459-
assertEquals("no-version-here", trimVersionSuffix("no-version-here"));
460-
assertNull(trimVersionSuffix(null));
461-
assertEquals("", trimVersionSuffix(""));
463+
assertEquals("15-15", ConversionUtils.trimVersionSuffix("15-15_v1.3"));
464+
assertEquals("15-nexsis", ConversionUtils.trimVersionSuffix("15-nexsis_v2"));
465+
assertEquals("backup", ConversionUtils.trimVersionSuffix("backup_v2.0.1"));
466+
assertEquals("no-version-here", ConversionUtils.trimVersionSuffix("no-version-here"));
467+
assertNull(ConversionUtils.trimVersionSuffix(null));
468+
assertEquals("", ConversionUtils.trimVersionSuffix(""));
462469
}
463470

464471
@Test
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright © 2023-2026 Agence du Numerique en Sante (ANS)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.hubsante.hub.service;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertNull;
20+
import static org.junit.jupiter.api.Assertions.assertThrows;
21+
22+
import com.hubsante.hub.exception.ClientConfigurationException;
23+
import org.junit.jupiter.api.DisplayName;
24+
import org.junit.jupiter.api.Test;
25+
import org.springframework.core.io.ClassPathResource;
26+
import org.springframework.core.io.Resource;
27+
28+
public class TopologyRegistryTest {
29+
30+
@Test
31+
@DisplayName("should load topology configuration, including dotted vhost keys")
32+
void shouldLoadTopologyConfiguration() {
33+
Resource resource = new ClassPathResource("config/clients.yaml");
34+
TopologyRegistry topologyRegistry = new TopologyRegistry(resource);
35+
36+
assertEquals("v1", topologyRegistry.getMajorModelVersion("15-15_v1.5"));
37+
assertEquals("v3", topologyRegistry.getMajorModelVersion("15-nexsis_v1.9"));
38+
assertNull(topologyRegistry.getMajorModelVersion("unknown-vhost"));
39+
40+
assertEquals("15-nexsis_v1.9", topologyRegistry.getVhostTarget("fire"));
41+
assertNull(topologyRegistry.getVhostTarget("unknown-partner"));
42+
}
43+
44+
@Test
45+
@DisplayName("should fail to load when the topology block is missing")
46+
void shouldThrowWhenTopologyBlockMissing() {
47+
Resource resource = new ClassPathResource("config/invalid-clients-no-perimeters.yaml");
48+
49+
assertThrows(ClientConfigurationException.class, () -> new TopologyRegistry(resource));
50+
}
51+
52+
@Test
53+
@DisplayName("should fail to load when the 'fire' hubex partner entry is missing")
54+
void shouldThrowWhenFirePartnerMissing() {
55+
Resource resource =
56+
new ClassPathResource("config/invalid-clients-missing-fire-partner.yaml");
57+
58+
assertThrows(ClientConfigurationException.class, () -> new TopologyRegistry(resource));
59+
}
60+
}

hub/dispatcher/src/test/java/com/hubsante/hub/utils/MessagePersistencePolicyTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@
1818
import static org.junit.jupiter.api.Assertions.assertFalse;
1919
import static org.junit.jupiter.api.Assertions.assertTrue;
2020

21+
import com.hubsante.hub.service.TopologyRegistry;
22+
import org.junit.jupiter.api.BeforeAll;
2123
import org.junit.jupiter.api.DisplayName;
2224
import org.junit.jupiter.api.Test;
2325
import org.junit.jupiter.params.ParameterizedTest;
2426
import org.junit.jupiter.params.provider.ValueSource;
27+
import org.springframework.core.io.ClassPathResource;
2528

2629
public class MessagePersistencePolicyTest {
2730

31+
@BeforeAll
32+
static void setUpTopologyRegistry() {
33+
new TopologyRegistry(new ClassPathResource("config/clients.yaml"));
34+
}
35+
2836
// ─── Nexsis vhost (18 → 15) ───────────────────────────────────────────────
2937

3038
@Test

hub/dispatcher/src/test/resources/config/clients.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,11 @@ clients:
160160
- '2.0'
161161
sasPerimeterVersions:
162162
- '1.0'
163-
lrm_test: false
163+
lrm_test: false
164+
majorModelVersionPerVhost:
165+
15-15_v1.5: "v1"
166+
15-15_v2.0: "v2"
167+
15-15_v2.1: "v3"
168+
15-nexsis_v1.9: "v3"
169+
vhostTargetPerHubexPartner:
170+
fire: "15-nexsis_v1.9"

0 commit comments

Comments
 (0)