This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new e3c05af20c fix: register namespace-prefixed change listener prefixes
for apollo sync data (#6984)
e3c05af20c is described below
commit e3c05af20cb2ac1a6510bb9868ee169018064622
Author: wy471x <[email protected]>
AuthorDate: Wed Aug 26 15:35:51 2026 +0800
fix: register namespace-prefixed change listener prefixes for apollo sync
data (#6984)
Apollo's AbstractConfig matches interested key prefixes with
changedKey.startsWith(prefix), but real property keys are namespace-prefixed
(e.g. {namespaceId}.plugin.{pluginName}), so incremental change events were
silently dropped after the startup load.
- register prefixes built from namespaceId + dataId
- match change keys in the listener with startsWith(namespaceId + "." +
dataId)
- parse the plugin name from the remove key in unCachePluginData
- add integration test flowing through apollo-client's real listener
filtering
Co-authored-by: Claude <[email protected]>
Co-authored-by: aias00 <[email protected]>
---
.../common/constant/ApolloPathConstants.java | 13 ++
.../data/core/AbstractNodeDataSyncService.java | 9 +-
.../data/core/AbstractNodeDataSyncServiceTest.java | 9 +-
.../shenyu/sync/data/apollo/ApolloDataService.java | 22 ++--
.../apollo/ApolloDataServiceIntegrationTest.java | 141 +++++++++++++++++++++
.../sync/data/apollo/ApolloDataServiceTest.java | 112 ++++++++++++++--
6 files changed, 276 insertions(+), 30 deletions(-)
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/ApolloPathConstants.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/ApolloPathConstants.java
index e2a7c7d340..79bb4a9238 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/ApolloPathConstants.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/ApolloPathConstants.java
@@ -95,5 +95,18 @@ public class ApolloPathConstants {
return new HashSet<>(Arrays.asList(PLUGIN_DATA_ID, SELECTOR_DATA_ID,
RULE_DATA_ID, AUTH_DATA_ID,
META_DATA_ID, PROXY_SELECTOR_DATA_ID, DISCOVERY_DATA_ID));
}
+
+ /**
+ * get path key set with namespace prefix.
+ * for example, if the namespace id is {@code shenyu}, the plugin path key
is {@code shenyu.plugin}.
+ *
+ * @param namespaceId namespace id
+ * @return path key set with namespace prefix
+ */
+ public static Set<String> pathKeySet(final String namespaceId) {
+ final Set<String> namespacedPathKeySet = new HashSet<>();
+ pathKeySet().forEach(dataId -> namespacedPathKeySet.add(namespaceId +
DefaultNodeConstants.JOIN_POINT + dataId));
+ return namespacedPathKeySet;
+ }
}
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-api/src/main/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncService.java
b/shenyu-sync-data-center/shenyu-sync-data-api/src/main/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncService.java
index 17a0e6d5cf..98e6926143 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-api/src/main/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncService.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-api/src/main/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncService.java
@@ -216,9 +216,14 @@ public abstract class AbstractNodeDataSyncService {
.flatMap(data ->
Optional.ofNullable(pluginDataSubscriber)).ifPresent(e ->
e.onSubscribe(pluginData));
}
- protected void unCachePluginData(final String pluginName) {
+ protected void unCachePluginData(final String removeKey) {
+ final String[] pluginKeys = StringUtils.split(removeKey,
DefaultNodeConstants.JOIN_POINT);
+ if (Objects.isNull(pluginKeys) || pluginKeys.length < 3) {
+ LOG.warn("AbstractNodeDataSyncService invalid plugin data remove
key: {}", removeKey);
+ return;
+ }
final PluginData data = new PluginData();
- data.setName(pluginName);
+ data.setName(pluginKeys[2]);
Optional.ofNullable(pluginDataSubscriber).ifPresent(e ->
e.unSubscribe(data));
}
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-api/src/test/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncServiceTest.java
b/shenyu-sync-data-center/shenyu-sync-data-api/src/test/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncServiceTest.java
index 79178226d5..0673fd5079 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-api/src/test/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncServiceTest.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-api/src/test/java/org/apache/shenyu/sync/data/core/AbstractNodeDataSyncServiceTest.java
@@ -116,13 +116,11 @@ public class AbstractNodeDataSyncServiceTest {
@Test
public void testUnCachePluginData() {
- String pluginName = "testPlugin";
-
- nodeDataSyncService.unCachePluginData(pluginName);
+ nodeDataSyncService.unCachePluginData("namespace.plugin.testPlugin");
ArgumentCaptor<PluginData> captor =
ArgumentCaptor.forClass(PluginData.class);
verify(pluginDataSubscriber).unSubscribe(captor.capture());
- assertEquals(pluginName, captor.getValue().getName());
+ assertEquals("testPlugin", captor.getValue().getName());
}
@Test
@@ -165,11 +163,12 @@ public class AbstractNodeDataSyncServiceTest {
@Test
public void testUnCacheDataWithInvalidKey() {
+ assertDoesNotThrow(() ->
nodeDataSyncService.unCachePluginData("namespace"));
assertDoesNotThrow(() ->
nodeDataSyncService.unCacheAuthData("namespace"));
assertDoesNotThrow(() ->
nodeDataSyncService.unCacheMetaData("namespace"));
assertDoesNotThrow(() ->
nodeDataSyncService.unCacheProxySelectorData("namespace"));
- verifyNoInteractions(authDataSubscriber, metaDataSubscriber,
proxySelectorDataSubscriber);
+ verifyNoInteractions(pluginDataSubscriber, authDataSubscriber,
metaDataSubscriber, proxySelectorDataSubscriber);
}
// Mock implementation
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-apollo/src/main/java/org/apache/shenyu/sync/data/apollo/ApolloDataService.java
b/shenyu-sync-data-center/shenyu-sync-data-apollo/src/main/java/org/apache/shenyu/sync/data/apollo/ApolloDataService.java
index bc53f01ad1..e2f8680426 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-apollo/src/main/java/org/apache/shenyu/sync/data/apollo/ApolloDataService.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-apollo/src/main/java/org/apache/shenyu/sync/data/apollo/ApolloDataService.java
@@ -81,10 +81,12 @@ public class ApolloDataService extends
AbstractNodeDataSyncService implements Sy
this.configService = configService;
startWatch();
- apolloWatchPrefixes();
+ apolloWatchPrefixes(shenyuConfig.getNamespace());
}
- private void apolloWatchPrefixes() {
+ private void apolloWatchPrefixes(final String namespaceId) {
+ // apollo change keys are namespace-prefixed, e.g.
{namespaceId}.plugin.{pluginName}
+ final String namespacePrefix = namespaceId +
DefaultNodeConstants.JOIN_POINT;
final ConfigChangeListener listener = changeEvent ->
changeEvent.changedKeys().forEach(changeKey -> {
try {
final ConfigChange configChange =
changeEvent.getChange(changeKey);
@@ -99,43 +101,43 @@ public class ApolloDataService extends
AbstractNodeDataSyncService implements Sy
return;
}
// check prefix
- if (changeKey.indexOf(ApolloPathConstants.PLUGIN_DATA_ID) ==
0) {
+ if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.PLUGIN_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCachePluginData(changeKey);
} else {
cachePluginData(newValue);
}
- } else if
(changeKey.indexOf(ApolloPathConstants.SELECTOR_DATA_ID) == 0) {
+ } else if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.SELECTOR_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCacheSelectorData(changeKey);
} else {
cacheSelectorData(newValue);
}
- } else if (changeKey.indexOf(ApolloPathConstants.RULE_DATA_ID)
== 0) {
+ } else if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.RULE_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCacheRuleData(changeKey);
} else {
cacheRuleData(newValue);
}
- } else if (changeKey.indexOf(ApolloPathConstants.AUTH_DATA_ID)
== 0) {
+ } else if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.AUTH_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCacheAuthData(changeKey);
} else {
cacheAuthData(newValue);
}
- } else if (changeKey.indexOf(ApolloPathConstants.META_DATA_ID)
== 0) {
+ } else if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.META_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCacheMetaData(changeKey);
} else {
cacheMetaData(newValue);
}
- } else if
(changeKey.indexOf(ApolloPathConstants.PROXY_SELECTOR_DATA_ID) == 0) {
+ } else if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.PROXY_SELECTOR_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCacheProxySelectorData(changeKey);
} else {
cacheProxySelectorData(newValue);
}
- } else if
(changeKey.indexOf(ApolloPathConstants.DISCOVERY_DATA_ID) == 0) {
+ } else if (changeKey.startsWith(namespacePrefix +
ApolloPathConstants.DISCOVERY_DATA_ID)) {
if
(PropertyChangeType.DELETED.equals(configChange.getChangeType())) {
unCacheDiscoveryUpstreamData(changeKey);
} else {
@@ -147,7 +149,7 @@ public class ApolloDataService extends
AbstractNodeDataSyncService implements Sy
}
});
watchConfigChangeListener = listener;
- configService.addChangeListener(listener, Collections.emptySet(),
ApolloPathConstants.pathKeySet());
+ configService.addChangeListener(listener, Collections.emptySet(),
ApolloPathConstants.pathKeySet(namespaceId));
}
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceIntegrationTest.java
b/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceIntegrationTest.java
new file mode 100644
index 0000000000..eb99b83e05
--- /dev/null
+++
b/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceIntegrationTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.sync.data.apollo;
+
+import com.ctrip.framework.apollo.enums.ConfigSourceType;
+import com.ctrip.framework.apollo.internals.ConfigRepository;
+import com.ctrip.framework.apollo.internals.DefaultConfig;
+import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.sync.data.api.AuthDataSubscriber;
+import org.apache.shenyu.sync.data.api.DiscoveryUpstreamDataSubscriber;
+import org.apache.shenyu.sync.data.api.MetaDataSubscriber;
+import org.apache.shenyu.sync.data.api.PluginDataSubscriber;
+import org.apache.shenyu.sync.data.api.ProxySelectorDataSubscriber;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Integration test case for {@link ApolloDataService} that flows through
apollo-client's real
+ * change listener filtering (interested keys and prefixes are matched in
{@link com.ctrip.framework.apollo.internals.AbstractConfig}).
+ */
+@ExtendWith(MockitoExtension.class)
+class ApolloDataServiceIntegrationTest {
+
+ private static final String NAMESPACE = "shenyu";
+
+ @Mock
+ private PluginDataSubscriber pluginDataSubscriber;
+
+ @Mock
+ private MetaDataSubscriber metaDataSubscriber;
+
+ @Mock
+ private AuthDataSubscriber authDataSubscriber;
+
+ @Mock
+ private ProxySelectorDataSubscriber proxySelectorDataSubscriber;
+
+ @Mock
+ private DiscoveryUpstreamDataSubscriber discoveryUpstreamDataSubscriber;
+
+ @Test
+ void testIncrementalChangeEventFlowsThroughApolloPrefixFiltering() throws
InterruptedException {
+ final DefaultConfig defaultConfig = createDefaultConfig();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ doAnswer(invocation -> {
+ latch.countDown();
+ return null;
+ }).when(pluginDataSubscriber).onSubscribe(any());
+
+ new ApolloDataService(defaultConfig,
+ pluginDataSubscriber,
+ Collections.singletonList(metaDataSubscriber),
+ Collections.singletonList(authDataSubscriber),
+ Collections.singletonList(proxySelectorDataSubscriber),
+ Collections.singletonList(discoveryUpstreamDataSubscriber),
+ createShenyuConfig());
+
+ final Properties changedProperties = new Properties();
+ changedProperties.setProperty(NAMESPACE + ".plugin.divide",
"{\"id\":\"1\",\"name\":\"divide\"}");
+ defaultConfig.onRepositoryChange(NAMESPACE, changedProperties);
+
+ assertTrue(latch.await(5, TimeUnit.SECONDS),
+ "incremental change event should reach the subscriber through
apollo prefix filtering");
+ }
+
+ @Test
+ void testDeletedEventFlowsThroughApolloPrefixFiltering() throws
InterruptedException {
+ final DefaultConfig defaultConfig = createDefaultConfig();
+ final Properties initialProperties = new Properties();
+ initialProperties.setProperty(NAMESPACE + ".plugin.divide",
"{\"id\":\"1\",\"name\":\"divide\"}");
+ defaultConfig.onRepositoryChange(NAMESPACE, initialProperties);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ doAnswer(invocation -> {
+ latch.countDown();
+ return null;
+ }).when(pluginDataSubscriber).unSubscribe(any());
+
+ new ApolloDataService(defaultConfig,
+ pluginDataSubscriber,
+ Collections.singletonList(metaDataSubscriber),
+ Collections.singletonList(authDataSubscriber),
+ Collections.singletonList(proxySelectorDataSubscriber),
+ Collections.singletonList(discoveryUpstreamDataSubscriber),
+ createShenyuConfig());
+
+ defaultConfig.onRepositoryChange(NAMESPACE, new Properties());
+
+ assertTrue(latch.await(5, TimeUnit.SECONDS),
+ "deleted event should reach the subscriber through apollo
prefix filtering");
+ ArgumentCaptor<PluginData> captor =
ArgumentCaptor.forClass(PluginData.class);
+ verify(pluginDataSubscriber).unSubscribe(captor.capture());
+ assertEquals("divide", captor.getValue().getName());
+ }
+
+ private DefaultConfig createDefaultConfig() {
+ final ConfigRepository configRepository = mock(ConfigRepository.class);
+ when(configRepository.getConfig()).thenReturn(new Properties());
+
when(configRepository.getSourceType()).thenReturn(ConfigSourceType.LOCAL);
+ return new DefaultConfig(NAMESPACE, configRepository);
+ }
+
+ private ShenyuConfig createShenyuConfig() {
+ final ShenyuConfig shenyuConfig = new ShenyuConfig();
+ shenyuConfig.setNamespace(NAMESPACE);
+ return shenyuConfig;
+ }
+}
diff --git
a/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceTest.java
b/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceTest.java
index b2cbfad860..9db5392159 100644
---
a/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceTest.java
+++
b/shenyu-sync-data-center/shenyu-sync-data-apollo/src/test/java/org/apache/shenyu/sync/data/apollo/ApolloDataServiceTest.java
@@ -24,6 +24,9 @@ import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import org.apache.shenyu.common.config.ShenyuConfig;
import org.apache.shenyu.common.constant.ApolloPathConstants;
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.ProxySelectorData;
+import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.sync.data.api.AuthDataSubscriber;
import org.apache.shenyu.sync.data.api.DiscoveryUpstreamDataSubscriber;
import org.apache.shenyu.sync.data.api.MetaDataSubscriber;
@@ -40,6 +43,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anySet;
@@ -49,6 +53,7 @@ import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
/**
@@ -57,6 +62,8 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ApolloDataServiceTest {
+ private static final String NAMESPACE = "shenyu";
+
@Mock
private Config configService;
@@ -82,7 +89,7 @@ class ApolloDataServiceTest {
@BeforeEach
void setUp() {
shenyuConfig = new ShenyuConfig();
- shenyuConfig.setNamespace("shenyu");
+ shenyuConfig.setNamespace(NAMESPACE);
lenient().when(configService.getProperty(anyString(),
any())).thenReturn("[]");
}
@@ -100,7 +107,7 @@ class ApolloDataServiceTest {
);
ArgumentCaptor<ConfigChangeListener> listenerCaptor =
ArgumentCaptor.forClass(ConfigChangeListener.class);
- verify(configService).addChangeListener(listenerCaptor.capture(),
anySet(), eq(ApolloPathConstants.pathKeySet()));
+ verify(configService).addChangeListener(listenerCaptor.capture(),
anySet(), eq(ApolloPathConstants.pathKeySet(NAMESPACE)));
capturedListener = listenerCaptor.getValue();
assertNotNull(capturedListener);
@@ -132,7 +139,7 @@ class ApolloDataServiceTest {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.PLUGIN_DATA_ID + "/test-plugin",
+ NAMESPACE + "." + ApolloPathConstants.PLUGIN_DATA_ID +
".test-plugin",
"{\"id\":\"1\",\"name\":\"test\"}",
PropertyChangeType.MODIFIED
);
@@ -146,7 +153,7 @@ class ApolloDataServiceTest {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.PLUGIN_DATA_ID + "/new-plugin",
+ NAMESPACE + "." + ApolloPathConstants.PLUGIN_DATA_ID +
".new-plugin",
"{\"id\":\"2\",\"name\":\"new\"}",
PropertyChangeType.ADDED
);
@@ -155,12 +162,28 @@ class ApolloDataServiceTest {
verify(pluginDataSubscriber, times(1)).onSubscribe(any());
}
+ @Test
+ void testPluginDataDeleted() {
+ ApolloDataService apolloDataService = createApolloDataService();
+
+ ConfigChangeEvent event = mockConfigChangeEvent(
+ NAMESPACE + "." + ApolloPathConstants.PLUGIN_DATA_ID +
".test-plugin",
+ null,
+ PropertyChangeType.DELETED
+ );
+
+ capturedListener.onChange(event);
+ ArgumentCaptor<PluginData> captor =
ArgumentCaptor.forClass(PluginData.class);
+ verify(pluginDataSubscriber, times(1)).unSubscribe(captor.capture());
+ assertEquals("test-plugin", captor.getValue().getName());
+ }
+
@Test
void testSelectorDataChange() {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.SELECTOR_DATA_ID + "/test-selector",
+ NAMESPACE + "." + ApolloPathConstants.SELECTOR_DATA_ID +
".test-plugin.test-selector",
"{\"id\":\"1\",\"name\":\"test\"}",
PropertyChangeType.MODIFIED
);
@@ -174,7 +197,7 @@ class ApolloDataServiceTest {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.RULE_DATA_ID + "/test-rule",
+ NAMESPACE + "." + ApolloPathConstants.RULE_DATA_ID +
".test-plugin.test-selector.test-rule",
"{\"id\":\"1\",\"name\":\"test\"}",
PropertyChangeType.MODIFIED
);
@@ -183,12 +206,30 @@ class ApolloDataServiceTest {
verify(pluginDataSubscriber, times(1)).onRuleSubscribe(any());
}
+ @Test
+ void testRuleDataDeleted() {
+ ApolloDataService apolloDataService = createApolloDataService();
+
+ ConfigChangeEvent event = mockConfigChangeEvent(
+ NAMESPACE + "." + ApolloPathConstants.RULE_DATA_ID +
".test-plugin.test-selector.test-rule",
+ null,
+ PropertyChangeType.DELETED
+ );
+
+ capturedListener.onChange(event);
+ ArgumentCaptor<RuleData> captor =
ArgumentCaptor.forClass(RuleData.class);
+ verify(pluginDataSubscriber,
times(1)).unRuleSubscribe(captor.capture());
+ assertEquals("test-plugin", captor.getValue().getPluginName());
+ assertEquals("test-selector", captor.getValue().getSelectorId());
+ assertEquals("test-rule", captor.getValue().getId());
+ }
+
@Test
void testAuthDataChange() {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.AUTH_DATA_ID + "/test-auth",
+ NAMESPACE + "." + ApolloPathConstants.AUTH_DATA_ID +
".test-auth",
"{\"appKey\":\"test\"}",
PropertyChangeType.MODIFIED
);
@@ -202,7 +243,7 @@ class ApolloDataServiceTest {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.META_DATA_ID + "/test-meta",
+ NAMESPACE + "." + ApolloPathConstants.META_DATA_ID +
".test-meta",
"{\"id\":\"1\",\"path\":\"/test\"}",
PropertyChangeType.MODIFIED
);
@@ -216,7 +257,7 @@ class ApolloDataServiceTest {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.PROXY_SELECTOR_DATA_ID + "/test-proxy",
+ NAMESPACE + "." + ApolloPathConstants.PROXY_SELECTOR_DATA_ID +
".test-plugin.test-proxy",
"{\"id\":\"1\",\"name\":\"test\"}",
PropertyChangeType.MODIFIED
);
@@ -225,12 +266,29 @@ class ApolloDataServiceTest {
verify(proxySelectorDataSubscriber, times(1)).onSubscribe(any());
}
+ @Test
+ void testProxySelectorDataDeleted() {
+ ApolloDataService apolloDataService = createApolloDataService();
+
+ ConfigChangeEvent event = mockConfigChangeEvent(
+ NAMESPACE + "." + ApolloPathConstants.PROXY_SELECTOR_DATA_ID +
".test-plugin.test-proxy",
+ null,
+ PropertyChangeType.DELETED
+ );
+
+ capturedListener.onChange(event);
+ ArgumentCaptor<ProxySelectorData> captor =
ArgumentCaptor.forClass(ProxySelectorData.class);
+ verify(proxySelectorDataSubscriber,
times(1)).unSubscribe(captor.capture());
+ assertEquals("test-plugin", captor.getValue().getPluginName());
+ assertEquals("test-proxy", captor.getValue().getName());
+ }
+
@Test
void testDiscoveryDataChange() {
ApolloDataService apolloDataService = createApolloDataService();
ConfigChangeEvent event = mockConfigChangeEvent(
- ApolloPathConstants.DISCOVERY_DATA_ID + "/test-discovery",
+ NAMESPACE + "." + ApolloPathConstants.DISCOVERY_DATA_ID +
".test-plugin.test-discovery",
"{\"name\":\"test\"}",
PropertyChangeType.MODIFIED
);
@@ -239,6 +297,34 @@ class ApolloDataServiceTest {
verify(discoveryUpstreamDataSubscriber, times(1)).onSubscribe(any());
}
+ @Test
+ void testListKeySkipped() {
+ ApolloDataService apolloDataService = createApolloDataService();
+
+ ConfigChangeEvent event = mockConfigChangeEvent(
+ NAMESPACE + "." + ApolloPathConstants.PLUGIN_DATA_ID + ".list",
+ "[\"test-plugin\"]",
+ PropertyChangeType.MODIFIED
+ );
+
+ capturedListener.onChange(event);
+ verifyNoInteractions(pluginDataSubscriber);
+ }
+
+ @Test
+ void testChangeKeyWithoutNamespacePrefixIgnored() {
+ ApolloDataService apolloDataService = createApolloDataService();
+
+ ConfigChangeEvent event = mockConfigChangeEvent(
+ "other-namespace." + ApolloPathConstants.PLUGIN_DATA_ID +
".test-plugin",
+ "{\"id\":\"1\",\"name\":\"test\"}",
+ PropertyChangeType.MODIFIED
+ );
+
+ capturedListener.onChange(event);
+ verifyNoInteractions(pluginDataSubscriber);
+ }
+
@Test
void testNullConfigChange() {
final ApolloDataService apolloDataService = createApolloDataService();
@@ -260,7 +346,7 @@ class ApolloDataServiceTest {
ConfigChangeEvent event = mock(ConfigChangeEvent.class);
Set<String> keys = new HashSet<>();
- keys.add(ApolloPathConstants.PLUGIN_DATA_ID + "/test");
+ keys.add(NAMESPACE + "." + ApolloPathConstants.PLUGIN_DATA_ID +
".test");
when(event.changedKeys()).thenReturn(keys);
when(event.getChange(anyString())).thenThrow(new
RuntimeException("Test exception"));
@@ -281,7 +367,7 @@ class ApolloDataServiceTest {
);
ArgumentCaptor<ConfigChangeListener> listenerCaptor =
ArgumentCaptor.forClass(ConfigChangeListener.class);
- verify(configService).addChangeListener(listenerCaptor.capture(),
anySet(), eq(ApolloPathConstants.pathKeySet()));
+ verify(configService).addChangeListener(listenerCaptor.capture(),
anySet(), eq(ApolloPathConstants.pathKeySet(NAMESPACE)));
capturedListener = listenerCaptor.getValue();
return service;
@@ -297,7 +383,7 @@ class ApolloDataServiceTest {
when(event.changedKeys()).thenReturn(keys);
when(event.getChange(key)).thenReturn(configChange);
when(configChange.getNewValue()).thenReturn(newValue);
- when(configChange.getChangeType()).thenReturn(changeType);
+ lenient().when(configChange.getChangeType()).thenReturn(changeType);
return event;
}