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 76a0204b18 fix(admin): carry namespaceid on appauth delete events 
(#7962)
76a0204b18 is described below

commit 76a0204b1825054b81841efdba09231a646c1e88
Author: Duansg <[email protected]>
AuthorDate: Thu Oct 22 20:18:50 2026 -0700

    fix(admin): carry namespaceid on appauth delete events (#7962)
---
 .../admin/service/impl/AppAuthServiceImpl.java     |  11 +-
 .../shenyu/admin/service/AppAuthServiceTest.java   | 116 ++++++++++++++++++++-
 2 files changed, 119 insertions(+), 8 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/AppAuthServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/AppAuthServiceImpl.java
index 21ea38efb1..ab3e584748 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/AppAuthServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/AppAuthServiceImpl.java
@@ -46,6 +46,7 @@ import org.apache.shenyu.admin.service.AppAuthService;
 import org.apache.shenyu.admin.transfer.AppAuthTransfer;
 import org.apache.shenyu.admin.utils.ShenyuResultMessage;
 import org.apache.shenyu.common.constant.AdminConstants;
+import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.dto.AppAuthData;
 import org.apache.shenyu.common.dto.AuthParamData;
 import org.apache.shenyu.common.dto.AuthPathData;
@@ -464,16 +465,18 @@ public class AppAuthServiceImpl implements AppAuthService 
{
         authParamMapper.deleteByAuthIds(ids);
         authPathMapper.deleteByAuthIds(ids);
 
-        List<AppAuthData> appAuthData = appAuthList.stream().map(appAuthDO -> 
AppAuthData.builder()
+        Map<String, List<AppAuthData>> namespaceDataList = 
appAuthList.stream().map(appAuthDO -> AppAuthData.builder()
+                
.namespaceId(StringUtils.defaultString(appAuthDO.getNamespaceId(), 
Constants.SYS_DEFAULT_NAMESPACE_ID))
                 .appKey(appAuthDO.getAppKey())
                 .appSecret(appAuthDO.getAppSecret())
                 .open(appAuthDO.getOpen())
                 .enabled(appAuthDO.getEnabled())
                 .paramDataList(null)
                 .pathDataList(null)
-                .build()).collect(Collectors.toCollection(() -> new 
ArrayList<>(appAuthList.size())));
-        // publish delete event of AppAuthData
-        eventPublisher.publishEvent(new 
DataChangedEvent(ConfigGroupEnum.APP_AUTH, DataEventTypeEnum.DELETE, 
appAuthData));
+                
.build()).collect(Collectors.groupingBy(AppAuthData::getNamespaceId));
+        // publish a separate delete event for each namespace
+        namespaceDataList.forEach((namespaceId, dataList) -> 
eventPublisher.publishEvent(
+                new DataChangedEvent(ConfigGroupEnum.APP_AUTH, 
DataEventTypeEnum.DELETE, dataList, namespaceId)));
 
         return affectCount;
     }
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/AppAuthServiceTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/AppAuthServiceTest.java
index 1888e3ec90..d14a888c1f 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/AppAuthServiceTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/AppAuthServiceTest.java
@@ -44,27 +44,34 @@ import org.apache.shenyu.admin.utils.ShenyuResultMessage;
 import org.apache.shenyu.common.constant.AdminConstants;
 import org.apache.shenyu.common.dto.AppAuthData;
 import org.apache.shenyu.common.enums.ConfigGroupEnum;
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
 import org.apache.shenyu.common.exception.CommonErrorCode;
 import org.apache.shenyu.common.utils.SignUtils;
 import org.apache.shenyu.common.utils.UUIDUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
+import org.junit.jupiter.params.provider.ValueSource;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.test.util.ReflectionTestUtils;
 
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
@@ -192,10 +199,103 @@ public final class AppAuthServiceTest {
         assertThat(this.appAuthService.createOrUpdate(updateAppAuthDTO), 
greaterThan(0));
     }
 
+    @ParameterizedTest
+    @ValueSource(strings = {"ns-A", SYS_DEFAULT_NAMESPACE_ID})
+    public void testDeletePreservesNamespace(final String namespaceId) {
+        appAuthDO.setNamespaceId(namespaceId);
+        List<String> ids = Collections.singletonList(appAuthDO.getId());
+        
given(appAuthMapper.selectByIds(ids)).willReturn(Collections.singletonList(appAuthDO));
+        given(appAuthMapper.deleteByIds(ids)).willReturn(1);
+
+        assertEquals(1, appAuthService.delete(ids));
+
+        verify(authParamMapper).deleteByAuthIds(ids);
+        verify(authPathMapper).deleteByAuthIds(ids);
+        ArgumentCaptor<DataChangedEvent> eventCaptor = 
ArgumentCaptor.forClass(DataChangedEvent.class);
+        verify(eventPublisher).publishEvent(eventCaptor.capture());
+        assertDeleteEvent(eventCaptor.getValue(), namespaceId, 
appAuthDO.getAppKey());
+    }
+
+    @Test
+    public void testDeletePublishesOneEventPerNamespace() {
+        AppAuthDO first = buildAppAuthDO("auth-A1", "ns-A");
+        AppAuthDO second = buildAppAuthDO("auth-B", "ns-B");
+        AppAuthDO third = buildAppAuthDO("auth-A2", "ns-A");
+        AppAuthDO defaultAuth = buildAppAuthDO("auth-default", 
SYS_DEFAULT_NAMESPACE_ID);
+        List<AppAuthDO> authList = List.of(first, second, third, defaultAuth);
+        List<String> ids = 
authList.stream().map(AppAuthDO::getId).collect(Collectors.toList());
+        given(appAuthMapper.selectByIds(ids)).willReturn(authList);
+        given(appAuthMapper.deleteByIds(ids)).willReturn(authList.size());
+
+        assertEquals(authList.size(), appAuthService.delete(ids));
+
+        verify(authParamMapper).deleteByAuthIds(ids);
+        verify(authPathMapper).deleteByAuthIds(ids);
+        ArgumentCaptor<DataChangedEvent> eventCaptor = 
ArgumentCaptor.forClass(DataChangedEvent.class);
+        verify(eventPublisher, times(3)).publishEvent(eventCaptor.capture());
+        Map<String, DataChangedEvent> events = 
eventCaptor.getAllValues().stream()
+                .collect(Collectors.toMap(DataChangedEvent::getNamespaceId, 
event -> event));
+        assertEquals(Set.of("ns-A", "ns-B", SYS_DEFAULT_NAMESPACE_ID), 
events.keySet());
+        assertDeleteEvent(events.get("ns-A"), "ns-A", first.getAppKey(), 
third.getAppKey());
+        assertDeleteEvent(events.get("ns-B"), "ns-B", second.getAppKey());
+        assertDeleteEvent(events.get(SYS_DEFAULT_NAMESPACE_ID), 
SYS_DEFAULT_NAMESPACE_ID, defaultAuth.getAppKey());
+    }
+
+    @Test
+    public void testDeleteFallsBackToDefaultNamespaceWhenMissing() {
+        AppAuthDO legacyAuth = buildAppAuthDO("auth-legacy", null);
+        AppAuthDO defaultAuth = buildAppAuthDO("auth-default", 
SYS_DEFAULT_NAMESPACE_ID);
+        List<AppAuthDO> authList = List.of(legacyAuth, defaultAuth);
+        List<String> ids = 
authList.stream().map(AppAuthDO::getId).collect(Collectors.toList());
+        given(appAuthMapper.selectByIds(ids)).willReturn(authList);
+        given(appAuthMapper.deleteByIds(ids)).willReturn(authList.size());
+
+        assertEquals(authList.size(), appAuthService.delete(ids));
+
+        ArgumentCaptor<DataChangedEvent> eventCaptor = 
ArgumentCaptor.forClass(DataChangedEvent.class);
+        verify(eventPublisher).publishEvent(eventCaptor.capture());
+        assertDeleteEvent(eventCaptor.getValue(), SYS_DEFAULT_NAMESPACE_ID, 
legacyAuth.getAppKey(), defaultAuth.getAppKey());
+    }
+
+    @ParameterizedTest
+    @NullAndEmptySource
+    public void testDeleteWithoutIdsDoesNotPublish(final List<String> ids) {
+        assertEquals(0, appAuthService.delete(ids));
+
+        verifyNoInteractions(appAuthMapper, authParamMapper, authPathMapper, 
eventPublisher);
+    }
+
     @Test
-    public void testDelete() {
-        int count = 
appAuthService.delete(Collections.singletonList(appAuthDO.getId()));
-        assertThat(count, greaterThanOrEqualTo(0));
+    public void testDeleteMissingRecordsDoesNotPublish() {
+        List<String> ids = Collections.singletonList(appAuthDO.getId());
+        
given(appAuthMapper.selectByIds(ids)).willReturn(Collections.emptyList());
+
+        assertEquals(0, appAuthService.delete(ids));
+
+        verify(appAuthMapper, never()).deleteByIds(any());
+        verifyNoInteractions(authParamMapper, authPathMapper, eventPublisher);
+    }
+
+    @ParameterizedTest
+    @ValueSource(ints = {0, -1})
+    public void testDeleteWithoutAffectedRowsDoesNotPublish(final int 
affectCount) {
+        List<String> ids = Collections.singletonList(appAuthDO.getId());
+        
given(appAuthMapper.selectByIds(ids)).willReturn(Collections.singletonList(appAuthDO));
+        given(appAuthMapper.deleteByIds(ids)).willReturn(affectCount);
+
+        assertEquals(affectCount, appAuthService.delete(ids));
+
+        verifyNoInteractions(authParamMapper, authPathMapper, eventPublisher);
+    }
+
+    private void assertDeleteEvent(final DataChangedEvent event, final String 
namespaceId, final String... appKeys) {
+        assertEquals(ConfigGroupEnum.APP_AUTH, event.getGroupKey());
+        assertEquals(DataEventTypeEnum.DELETE, 
ReflectionTestUtils.invokeMethod(event, "getEventType"));
+        assertEquals(namespaceId, event.getNamespaceId());
+        assertEquals(appKeys.length, event.getSource().size());
+        List<AppAuthData> authData = 
event.getSource().stream().map(AppAuthData.class::cast).collect(Collectors.toList());
+        authData.forEach(data -> assertEquals(namespaceId, 
data.getNamespaceId()));
+        assertEquals(Set.of(appKeys), 
authData.stream().map(AppAuthData::getAppKey).collect(Collectors.toSet()));
     }
 
     @Test
@@ -461,6 +561,14 @@ public final class AppAuthServiceTest {
         return appAuthDO;
     }
 
+    private AppAuthDO buildAppAuthDO(final String id, final String 
namespaceId) {
+        AppAuthDO auth = buildAppAuthDO();
+        auth.setId(id);
+        auth.setAppKey(id + "-key");
+        auth.setNamespaceId(namespaceId);
+        return auth;
+    }
+
     private AuthParamDTO buildAuthParamDTO() {
         AuthParamDTO authParamDTO = new AuthParamDTO();
         authParamDTO.setAppName("testAppName");

Reply via email to