From 28bf0f117568797852ba24c4313a919d26f52cce Mon Sep 17 00:00:00 2001 From: iacsha <122627209+iacsha@users.noreply.github.com> Date: Sat, 7 Nov 2026 00:39:40 +0000 Subject: [PATCH] Limit deploying and undeploying channels to the requested IDs (fixes #456) getDashboardChannels filtered deployed channels by the requested channel IDs but added every deploying and undeploying channel unfiltered. A request for channel A made while channel B was mid-deploy or mid-undeploy therefore included B, and GET /channels/{A}/statistics, which returns the first entry, could describe B. Apply the same filter to the transitional sets. The collection logic moves into a package-private static helper so it can be unit tested. Signed-off-by: iacsha <122627209+iacsha@users.noreply.github.com> --- .../controllers/DonkeyEngineController.java | 21 +++-- ...EngineControllerDashboardChannelsTest.java | 85 +++++++++++++++++++ 2 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 server/src/test/java/com/mirth/connect/server/controllers/DonkeyEngineControllerDashboardChannelsTest.java diff --git a/server/src/main/java/com/mirth/connect/server/controllers/DonkeyEngineController.java b/server/src/main/java/com/mirth/connect/server/controllers/DonkeyEngineController.java index d66185033..07d5bd487 100644 --- a/server/src/main/java/com/mirth/connect/server/controllers/DonkeyEngineController.java +++ b/server/src/main/java/com/mirth/connect/server/controllers/DonkeyEngineController.java @@ -723,23 +723,34 @@ public List getChannelStatusList(Set channelIds) { } private Map getDashboardChannels(Set channelIds) { + return getDashboardChannels(donkey.getDeployedChannels(), deployingChannels, undeployingChannels, channelIds); + } + + /** + * Collects the deployed, deploying and undeploying channels, limited to {@code channelIds} + * when it is not empty. The deploying and undeploying channels must honor the same filter, or + * a request for one channel returns another channel that is mid-deploy or mid-undeploy. See + * OpenIntegrationEngine/engine#456. + */ + static Map getDashboardChannels(Map deployedChannels, Set deployingChannels, Set undeployingChannels, Set channelIds) { + boolean filtered = CollectionUtils.isNotEmpty(channelIds); Map channels = null; - if (CollectionUtils.isNotEmpty(channelIds)) { + if (filtered) { channels = new HashMap(); - for (Channel channel : donkey.getDeployedChannels().values()) { + for (Channel channel : deployedChannels.values()) { if (channelIds.contains(channel.getChannelId())) { channels.put(channel.getChannelId(), channel); } } } else { - channels = new HashMap(donkey.getDeployedChannels()); + channels = new HashMap(deployedChannels); } synchronized (deployingChannels) { for (Channel channel : deployingChannels) { - if (!channels.containsKey(channel.getChannelId())) { + if ((!filtered || channelIds.contains(channel.getChannelId())) && !channels.containsKey(channel.getChannelId())) { channels.put(channel.getChannelId(), channel); } } @@ -747,7 +758,7 @@ private Map getDashboardChannels(Set channelIds) { synchronized (undeployingChannels) { for (Channel channel : undeployingChannels) { - if (!channels.containsKey(channel.getChannelId())) { + if ((!filtered || channelIds.contains(channel.getChannelId())) && !channels.containsKey(channel.getChannelId())) { channels.put(channel.getChannelId(), channel); } } diff --git a/server/src/test/java/com/mirth/connect/server/controllers/DonkeyEngineControllerDashboardChannelsTest.java b/server/src/test/java/com/mirth/connect/server/controllers/DonkeyEngineControllerDashboardChannelsTest.java new file mode 100644 index 000000000..537e17499 --- /dev/null +++ b/server/src/test/java/com/mirth/connect/server/controllers/DonkeyEngineControllerDashboardChannelsTest.java @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Open Integration Engine Contributors + +package com.mirth.connect.server.controllers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; + +import com.mirth.connect.donkey.server.channel.Channel; + +/** + * A request for specific channels must not return other channels that are mid-deploy or + * mid-undeploy. See OpenIntegrationEngine/engine#456. + */ +public class DonkeyEngineControllerDashboardChannelsTest { + + private static Channel channel(String id) { + Channel channel = new Channel(); + channel.setChannelId(id); + return channel; + } + + private static Set setOf(Channel... channels) { + return Collections.synchronizedSet(new HashSet(Arrays.asList(channels))); + } + + @Test + public void filteredRequestIgnoresOtherDeployingChannel() { + Channel a = channel("A"); + Map deployed = new HashMap(); + deployed.put("A", a); + + Map result = DonkeyEngineController.getDashboardChannels(deployed, setOf(channel("B")), setOf(), Collections.singleton("A")); + + assertEquals(Collections.singleton("A"), result.keySet()); + assertSame(a, result.get("A")); + } + + @Test + public void filteredRequestIgnoresOtherUndeployingChannel() { + Map result = DonkeyEngineController.getDashboardChannels(new HashMap(), setOf(), setOf(channel("B")), Collections.singleton("A")); + + assertEquals(Collections. emptySet(), result.keySet()); + } + + @Test + public void filteredRequestIncludesRequestedChannelWhileItDeploys() { + Channel a = channel("A"); + + Map result = DonkeyEngineController.getDashboardChannels(new HashMap(), setOf(a, channel("B")), setOf(channel("C")), Collections.singleton("A")); + + assertEquals(Collections.singleton("A"), result.keySet()); + assertSame(a, result.get("A")); + } + + @Test + public void unfilteredRequestIncludesDeployingAndUndeployingChannels() { + Map deployed = new HashMap(); + deployed.put("A", channel("A")); + + Map result = DonkeyEngineController.getDashboardChannels(deployed, setOf(channel("B")), setOf(channel("C")), null); + + assertEquals(new HashSet(Arrays.asList("A", "B", "C")), result.keySet()); + } + + @Test + public void deployedChannelWinsOverSameIdInTransition() { + Channel deployedA = channel("A"); + Map deployed = new HashMap(); + deployed.put("A", deployedA); + + Map result = DonkeyEngineController.getDashboardChannels(deployed, setOf(channel("A")), setOf(), Collections.singleton("A")); + + assertSame(deployedA, result.get("A")); + } +}