Skip to content

Commit 7d7702f

Browse files
committed
global exception refined
1 parent b33141c commit 7d7702f

File tree

3 files changed

+76
-11
lines changed

3 files changed

+76
-11
lines changed

backend-services/api-gateway/src/main/java/com/ai/qa/gateway/interfaces/controller/AuthGatewayController.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package com.ai.qa.gateway.interfaces.controller;
22

3+
import com.ai.qa.gateway.interfaces.dto.AuthResponseDTO;
34
import com.ai.qa.gateway.interfaces.dto.LoginGatewayRequestDTO;
45
import com.ai.qa.gateway.interfaces.dto.RegisterGatewayRequestDTO;
56
import com.ai.qa.gateway.interfaces.dto.common.ApiResponseDTO;
67
import com.ai.qa.gateway.interfaces.facade.AuthFacade;
78
import io.swagger.v3.oas.annotations.Operation;
89
import io.swagger.v3.oas.annotations.tags.Tag;
910
import lombok.RequiredArgsConstructor;
11+
import org.springframework.http.HttpStatus;
1012
import org.springframework.validation.annotation.Validated;
1113
import org.springframework.web.bind.annotation.PostMapping;
1214
import org.springframework.web.bind.annotation.RequestBody;
1315
import org.springframework.web.bind.annotation.RequestMapping;
1416
import org.springframework.web.bind.annotation.RestController;
17+
import org.springframework.web.server.ResponseStatusException;
1518
import reactor.core.publisher.Mono;
1619

1720
@Tag(name = "Gateway Auth", description = "Authentication proxy endpoints exposed by the gateway")
@@ -26,13 +29,25 @@ public class AuthGatewayController {
2629
@PostMapping("/login")
2730
public Mono<ApiResponseDTO<AuthResponseDTO>> login(@RequestBody @Validated LoginGatewayRequestDTO request) {
2831
return authFacade.login(request)
29-
.map(ApiResponseDTO::success);
32+
.map(ApiResponseDTO::success)
33+
.onErrorResume(ex -> Mono.just(transformError(ex)));
3034
}
3135

3236
@Operation(summary = "Gateway register", description = "Delegates registration requests to user-service-fyb.")
3337
@PostMapping("/register")
3438
public Mono<ApiResponseDTO<AuthResponseDTO>> register(@RequestBody @Validated RegisterGatewayRequestDTO request) {
3539
return authFacade.register(request)
36-
.map(ApiResponseDTO::success);
40+
.map(ApiResponseDTO::success)
41+
.onErrorResume(ex -> Mono.just(transformError(ex)));
42+
}
43+
44+
private ApiResponseDTO<AuthResponseDTO> transformError(Throwable throwable) {
45+
if (throwable instanceof ResponseStatusException responseStatusException) {
46+
HttpStatus status = (HttpStatus) responseStatusException.getStatusCode();
47+
String message = responseStatusException.getReason();
48+
return ApiResponseDTO.failure(status.value(), message != null ? message : status.getReasonPhrase());
49+
}
50+
return ApiResponseDTO.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(),
51+
throwable.getMessage() != null ? throwable.getMessage() : "Internal Server Error");
3752
}
3853
}

backend-services/api-gateway/src/main/java/com/ai/qa/gateway/interfaces/dto/common/ApiResponseDTO.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,13 @@ public static <T> ApiResponseDTO<T> success(T data) {
1919
response.setData(data);
2020
return response;
2121
}
22+
23+
public static <T> ApiResponseDTO<T> failure(int code, String message) {
24+
ApiResponseDTO<T> response = new ApiResponseDTO<>();
25+
response.setCode(code);
26+
response.setMessage(message);
27+
response.setSuccess(false);
28+
response.setData(null);
29+
return response;
30+
}
2231
}

backend-services/api-gateway/src/main/java/com/ai/qa/gateway/interfaces/facade/AuthFacade.java

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import com.ai.qa.gateway.interfaces.dto.UserProfileGatewayResponse;
1212
import lombok.RequiredArgsConstructor;
1313
import lombok.extern.slf4j.Slf4j;
14+
import org.springframework.http.HttpStatus;
1415
import org.springframework.stereotype.Service;
16+
import org.springframework.web.server.ResponseStatusException;
1517
import reactor.core.publisher.Mono;
1618
import reactor.core.scheduler.Schedulers;
1719

@@ -25,37 +27,44 @@ public class AuthFacade {
2527

2628
public Mono<AuthResponseDTO> login(LoginGatewayRequestDTO request) {
2729
return Mono.fromCallable(() -> userServiceClient.login(request))
28-
.subscribeOn(Schedulers.boundedElastic());
30+
.subscribeOn(Schedulers.boundedElastic())
31+
.onErrorMap(this::mapFeignException);
2932
}
3033

3134
public Mono<AuthResponseDTO> register(RegisterGatewayRequestDTO request) {
3235
return Mono.fromCallable(() -> userServiceClient.register(request))
33-
.subscribeOn(Schedulers.boundedElastic());
36+
.subscribeOn(Schedulers.boundedElastic())
37+
.onErrorMap(this::mapFeignException);
3438
}
3539

3640
public Mono<UserProfileGatewayResponse> profile(Long userId) {
3741
return Mono.fromCallable(() -> userServiceClient.profile(userId).getData())
38-
.subscribeOn(Schedulers.boundedElastic());
42+
.subscribeOn(Schedulers.boundedElastic())
43+
.onErrorMap(this::mapFeignException);
3944
}
4045

4146
public Mono<UserProfileGatewayResponse> updateNickname(Long userId, UpdateNicknameGatewayRequest request) {
4247
return Mono.fromCallable(() -> userServiceClient.updateNickname(userId, request).getData())
43-
.subscribeOn(Schedulers.boundedElastic());
48+
.subscribeOn(Schedulers.boundedElastic())
49+
.onErrorMap(this::mapFeignException);
4450
}
4551

4652
public Mono<List<ChatSessionResponseDTO>> sessions(Long userId) {
4753
return Mono.fromCallable(() -> userServiceClient.sessions(userId).getData())
48-
.subscribeOn(Schedulers.boundedElastic());
54+
.subscribeOn(Schedulers.boundedElastic())
55+
.onErrorMap(this::mapFeignException);
4956
}
5057

5158
public Mono<ChatSessionResponseDTO> createSession(Long userId, CreateSessionGatewayRequest request) {
5259
return Mono.fromCallable(() -> userServiceClient.createSession(userId, request).getData())
53-
.subscribeOn(Schedulers.boundedElastic());
60+
.subscribeOn(Schedulers.boundedElastic())
61+
.onErrorMap(this::mapFeignException);
5462
}
5563

5664
public Mono<ChatSessionResponseDTO> getSession(Long userId, Long sessionId) {
5765
return Mono.fromCallable(() -> userServiceClient.getSession(userId, sessionId).getData())
58-
.subscribeOn(Schedulers.boundedElastic());
66+
.subscribeOn(Schedulers.boundedElastic())
67+
.onErrorMap(this::mapFeignException);
5968
}
6069

6170
public Mono<Void> deleteSession(Long userId, Long sessionId) {
@@ -66,11 +75,43 @@ public Mono<Void> deleteSession(Long userId, Long sessionId) {
6675

6776
public Mono<List<ChatHistoryResponseDTO>> history(Long userId, Long sessionId, Integer limit) {
6877
return Mono.fromCallable(() -> userServiceClient.history(userId, sessionId, limit).getData())
69-
.subscribeOn(Schedulers.boundedElastic());
78+
.subscribeOn(Schedulers.boundedElastic())
79+
.onErrorMap(this::mapFeignException);
7080
}
7181

7282
public Mono<Boolean> isSessionOwnedBy(Long sessionId, Long userId) {
7383
return Mono.fromCallable(() -> Boolean.TRUE.equals(userServiceClient.isSessionOwnedBy(sessionId, userId).getData()))
74-
.subscribeOn(Schedulers.boundedElastic());
84+
.subscribeOn(Schedulers.boundedElastic())
85+
.onErrorMap(this::mapFeignException);
86+
}
87+
88+
private Throwable mapFeignException(Throwable throwable) {
89+
if (throwable instanceof feign.FeignException feignException) {
90+
HttpStatus status = HttpStatus.resolve(feignException.status());
91+
String message = extractMessage(feignException);
92+
if (status == null) {
93+
status = HttpStatus.INTERNAL_SERVER_ERROR;
94+
}
95+
return new ResponseStatusException(status, message, feignException);
96+
}
97+
return throwable;
98+
}
99+
100+
private String extractMessage(feign.FeignException feignException) {
101+
try {
102+
String content = feignException.contentUTF8();
103+
if (content == null || content.isBlank()) {
104+
return feignException.getMessage();
105+
}
106+
// Attempt to parse downstream ApiResponse structure
107+
java.util.Map<?, ?> map = new com.fasterxml.jackson.databind.ObjectMapper().readValue(content, java.util.Map.class);
108+
Object message = map.get("message");
109+
if (message != null) {
110+
return message.toString();
111+
}
112+
return content;
113+
} catch (Exception ex) {
114+
return feignException.getMessage();
115+
}
75116
}
76117
}

0 commit comments

Comments
 (0)