fix(token): align revoke endpoint with 204 contract

Change DELETE /api/v1/tokens/{id} to return HTTP 204 No Content so the backend matches the existing OpenAPI contract and the frontend delete flow no longer rejects successful revocations.

Add a controller regression test that verifies the endpoint returns 204 with an empty body and still delegates the revoke call to ApiTokenService. Verified with the targeted TokenControllerTest plus full server mvn test.
This commit is contained in:
yun-zhi-ztl 2026-03-13 11:49:57 +08:00
parent 383bc1edae
commit 556d556724
2 changed files with 67 additions and 3 deletions

View file

@ -4,11 +4,11 @@ import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
import com.iflytek.skillhub.auth.token.ApiTokenService;
import com.iflytek.skillhub.dto.ApiResponse;
import com.iflytek.skillhub.dto.ApiResponseFactory;
import com.iflytek.skillhub.dto.MessageResponse;
import com.iflytek.skillhub.dto.TokenCreateRequest;
import com.iflytek.skillhub.dto.TokenCreateResponse;
import com.iflytek.skillhub.dto.TokenSummaryResponse;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
@ -59,10 +59,10 @@ public class TokenController extends BaseApiController {
}
@DeleteMapping("/{id}")
public ApiResponse<MessageResponse> revoke(
public ResponseEntity<Void> revoke(
@AuthenticationPrincipal PlatformPrincipal principal,
@PathVariable Long id) {
apiTokenService.revokeToken(id, principal.userId());
return ok("response.success.revoked", new MessageResponse("Token revoked"));
return ResponseEntity.noContent().build();
}
}

View file

@ -0,0 +1,64 @@
package com.iflytek.skillhub.controller;
import com.iflytek.skillhub.TestRedisConfig;
import com.iflytek.skillhub.auth.device.DeviceAuthService;
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
import com.iflytek.skillhub.auth.token.ApiTokenService;
import com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import java.util.Set;
import static org.mockito.Mockito.verify;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Import(TestRedisConfig.class)
class TokenControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private NamespaceMemberRepository namespaceMemberRepository;
@MockBean
private DeviceAuthService deviceAuthService;
@MockBean
private ApiTokenService apiTokenService;
@Test
void revoke_returns204NoContent() throws Exception {
PlatformPrincipal principal = new PlatformPrincipal(
"user-42", "tester", "tester@example.com", "", "github", Set.of("USER")
);
var auth = new UsernamePasswordAuthenticationToken(
principal, null, List.of(new SimpleGrantedAuthority("ROLE_USER"))
);
mockMvc.perform(delete("/api/v1/tokens/7")
.with(authentication(auth))
.with(csrf()))
.andExpect(status().isNoContent())
.andExpect(content().string(""));
verify(apiTokenService).revokeToken(7L, "user-42");
}
}