Skip to content

Commit 64df086

Browse files
MayuriXxromainVanheeCopilot
authored
feat(get_user_by_id): Add endpoint get user by id in database (#25)
* feat(putUser): add the api route to update a user * feat(putUser): add test for the service and update readme * feat(getUserById): add endpoint get user by id * feat(getUserById): rebase Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat(getUserById): add endpoint get user by id --------- Co-authored-by: Romain Vanhee <romain.vanhee@yrycom.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 8435e14 commit 64df086

8 files changed

Lines changed: 715 additions & 181 deletions

File tree

README.md

Lines changed: 458 additions & 169 deletions
Large diffs are not rendered by default.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.xpeho.spring_boot_java_random_user.domain.usecases;
2+
3+
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
4+
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
5+
import com.xpeho.spring_boot_java_random_user.domain.services.UserService;
6+
import org.springframework.stereotype.Service;
7+
8+
@Service
9+
public class GetUserByIdUseCase {
10+
private final UserService userService;
11+
12+
public GetUserByIdUseCase(UserService userService) {
13+
this.userService = userService;
14+
}
15+
16+
public UserEntity execute(long id) {
17+
return userService.getById(id)
18+
.orElseThrow(() -> new UserNotFoundException(id));
19+
}
20+
}

src/main/java/com/xpeho/spring_boot_java_random_user/domain/usecases/UpdateRandomUserUseCase.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ public UpdateRandomUserUseCase(UserService userService) {
1616

1717
public UserEntity execute(int id, UserRequest user) {
1818
UserEntity existingUser = userService.getById(id)
19-
.orElseThrow(() -> new UserNotFoundException(id));
19+
.orElseThrow(() -> new UserNotFoundException(id));
2020

2121
UserEntity updatedUser = new UserEntity(
22-
existingUser.id(),
23-
user.gender(),
24-
user.firstname(),
25-
user.lastname(),
26-
user.civility(),
27-
user.email(),
28-
user.phone(),
29-
user.picture(),
30-
user.nat()
22+
existingUser.id(),
23+
user.gender(),
24+
user.firstname(),
25+
user.lastname(),
26+
user.civility(),
27+
user.email(),
28+
user.phone(),
29+
user.picture(),
30+
user.nat()
3131
);
3232

3333
return userService.save(updatedUser);

src/main/java/com/xpeho/spring_boot_java_random_user/presentation/controllers/UserController.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,23 @@ ResponseEntity<List<UserEntity>> getRandomUsers(
4141
@Max(5000)
4242
int count
4343
);
44+
45+
@GetMapping("/{id}")
46+
@Operation(
47+
summary = "Get user by id",
48+
description = "Given a user by id, return the user if it exists in the database",
49+
parameters = {
50+
@Parameter(name = "id", description = "id of the requested user")
51+
}
52+
)
53+
@ApiResponse(responseCode = "200", description = "User successfully found and returned")
54+
@ApiResponse(responseCode = "404", description = "The requested user does not exist")
55+
@ApiResponse(responseCode = "500", description = "Internal server error")
56+
ResponseEntity<UserEntity> getUserById(
57+
@PathVariable
58+
int id
59+
);
60+
4461
@PutMapping("/{id}")
4562
@Operation(
4663
summary = "Modify a random user",

src/main/java/com/xpeho/spring_boot_java_random_user/presentation/handlers/UserHandler.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.xpeho.spring_boot_java_random_user.domain.entities.UserRequest;
55
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
66
import com.xpeho.spring_boot_java_random_user.domain.usecases.FetchAndSaveRandomUsersUseCase;
7+
import com.xpeho.spring_boot_java_random_user.domain.usecases.GetUserByIdUseCase;
78
import com.xpeho.spring_boot_java_random_user.domain.usecases.UpdateRandomUserUseCase;
89
import com.xpeho.spring_boot_java_random_user.presentation.controllers.UserController;
910
import org.slf4j.Logger;
@@ -24,13 +25,16 @@ public class UserHandler implements UserController {
2425

2526
private final FetchAndSaveRandomUsersUseCase fetchAndSaveRandomUsersUseCase;
2627
private final UpdateRandomUserUseCase updateRandomUserUseCase;
28+
private final GetUserByIdUseCase getUserByIdUseCase;
2729

2830
public UserHandler(
2931
FetchAndSaveRandomUsersUseCase fetchAndSaveRandomUsersUseCase,
30-
UpdateRandomUserUseCase updateRandomUserUseCase
32+
UpdateRandomUserUseCase updateRandomUserUseCase,
33+
GetUserByIdUseCase getUserByIdUseCase
3134
) {
3235
this.fetchAndSaveRandomUsersUseCase = fetchAndSaveRandomUsersUseCase;
3336
this.updateRandomUserUseCase = updateRandomUserUseCase;
37+
this.getUserByIdUseCase = getUserByIdUseCase;
3438
}
3539

3640
@Override
@@ -55,4 +59,15 @@ public ResponseEntity<UserEntity> updateRandomUser(int id, UserRequest user) {
5559
}
5660
}
5761

62+
@Override
63+
public ResponseEntity<UserEntity> getUserById(int id) {
64+
try {
65+
UserEntity user = getUserByIdUseCase.execute(id);
66+
return ResponseEntity.ok(user);
67+
} catch (UserNotFoundException e) {
68+
logger.warn("warning: the requested user does not exist : {}", e.getMessage(), e);
69+
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
70+
}
71+
}
72+
5873
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.xpeho.spring_boot_java_random_user.domain.usecases;
2+
3+
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
4+
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
5+
import com.xpeho.spring_boot_java_random_user.domain.services.UserService;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
10+
import java.util.Optional;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
import static org.mockito.Mockito.*;
14+
15+
class GetUserByIdUseCaseTest {
16+
private UserService userService;
17+
private GetUserByIdUseCase useCase;
18+
19+
@BeforeEach
20+
void setUp() {
21+
userService = mock(UserService.class);
22+
useCase = new GetUserByIdUseCase(userService);
23+
}
24+
25+
@Test
26+
@DisplayName("Should return user when user exists")
27+
void shouldReturnUserWhenFound() {
28+
UserEntity expected = new UserEntity(1L, "male", "John", "Doe", "Mr", "john@example.com", "0600000000", "http://pic.jpg", "FR");
29+
when(userService.getById(1L)).thenReturn(Optional.of(expected));
30+
31+
UserEntity result = useCase.execute(1L);
32+
33+
assertEquals(expected, result);
34+
verify(userService).getById(1L);
35+
}
36+
37+
@Test
38+
@DisplayName("Should throw UserNotFoundException when user does not exist")
39+
void shouldThrowUserNotFoundExceptionWhenUserDoesNotExist() {
40+
when(userService.getById(99L)).thenReturn(Optional.empty());
41+
42+
UserNotFoundException exception = assertThrows(
43+
UserNotFoundException.class,
44+
() -> useCase.execute(99L)
45+
);
46+
47+
assertTrue(exception.getMessage().contains("99"));
48+
verify(userService).getById(99L);
49+
}
50+
51+
@Test
52+
@DisplayName("Should call userService exactly once with the given id")
53+
void shouldCallUserServiceOnce() {
54+
UserEntity user = new UserEntity(5L, "female", "Alice", "Smith", "Ms", "alice@example.com", "0611111111", "http://pic2.jpg", "US");
55+
when(userService.getById(5L)).thenReturn(Optional.of(user));
56+
57+
useCase.execute(5L);
58+
59+
verify(userService, times(1)).getById(5L);
60+
verifyNoMoreInteractions(userService);
61+
}
62+
63+
@Test
64+
@DisplayName("Should not call userService with a different id")
65+
void shouldNotCallUserServiceWithDifferentId() {
66+
UserEntity user = new UserEntity(3L, "male", "Bob", "Brown", "Mr", "bob@example.com", "0622222222", "http://pic3.jpg", "DE");
67+
when(userService.getById(3L)).thenReturn(Optional.of(user));
68+
69+
useCase.execute(3L);
70+
71+
verify(userService, never()).getById(42L);
72+
}
73+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package com.xpeho.spring_boot_java_random_user.presentation;
2+
3+
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
4+
import com.xpeho.spring_boot_java_random_user.domain.entities.UserRequest;
5+
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
6+
import com.xpeho.spring_boot_java_random_user.domain.usecases.FetchAndSaveRandomUsersUseCase;
7+
import com.xpeho.spring_boot_java_random_user.domain.usecases.GetUserByIdUseCase;
8+
import com.xpeho.spring_boot_java_random_user.domain.usecases.UpdateRandomUserUseCase;
9+
import com.xpeho.spring_boot_java_random_user.presentation.handlers.UserHandler;
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.DisplayName;
12+
import org.junit.jupiter.api.Test;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.http.ResponseEntity;
15+
16+
import java.io.IOException;
17+
import java.util.List;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNull;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.times;
24+
import static org.mockito.Mockito.verify;
25+
import static org.mockito.Mockito.when;
26+
27+
class UserHandlerTest {
28+
29+
private FetchAndSaveRandomUsersUseCase fetchAndSaveRandomUsersUseCase;
30+
private UpdateRandomUserUseCase updateRandomUserUseCase;
31+
private GetUserByIdUseCase getUserByIdUseCase;
32+
private UserHandler userHandler;
33+
34+
@BeforeEach
35+
void setUp() {
36+
fetchAndSaveRandomUsersUseCase = mock(FetchAndSaveRandomUsersUseCase.class);
37+
updateRandomUserUseCase = mock(UpdateRandomUserUseCase.class);
38+
getUserByIdUseCase = mock(GetUserByIdUseCase.class);
39+
userHandler = new UserHandler(fetchAndSaveRandomUsersUseCase, updateRandomUserUseCase, getUserByIdUseCase);
40+
}
41+
42+
@Test
43+
@DisplayName("Should return 200 and users when getRandomUsers succeeds")
44+
void shouldReturnOkWhenGetRandomUsersSucceeds() throws IOException {
45+
List<UserEntity> users = List.of(
46+
new UserEntity(1L, "male", "John", "Doe", "Mr", "john@example.com", "0600000000", "pic.jpg", "FR")
47+
);
48+
when(fetchAndSaveRandomUsersUseCase.execute(2)).thenReturn(users);
49+
50+
ResponseEntity<List<UserEntity>> response = userHandler.getRandomUsers(2);
51+
52+
assertEquals(HttpStatus.OK, response.getStatusCode());
53+
assertEquals(users, response.getBody());
54+
verify(fetchAndSaveRandomUsersUseCase, times(1)).execute(2);
55+
}
56+
57+
@Test
58+
@DisplayName("Should return 500 and empty list when getRandomUsers throws IOException")
59+
void shouldReturnInternalServerErrorWhenGetRandomUsersFails() throws IOException {
60+
when(fetchAndSaveRandomUsersUseCase.execute(5)).thenThrow(new IOException("downstream unavailable"));
61+
62+
ResponseEntity<List<UserEntity>> response = userHandler.getRandomUsers(5);
63+
64+
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
65+
assertTrue(response.getBody() != null && response.getBody().isEmpty());
66+
verify(fetchAndSaveRandomUsersUseCase, times(1)).execute(5);
67+
}
68+
69+
@Test
70+
@DisplayName("Should return 200 and user when getUserById succeeds")
71+
void shouldReturnOkWhenGetUserByIdSucceeds() {
72+
UserEntity user = new UserEntity(42L, "female", "Alice", "Smith", "Ms", "alice@example.com", "0611111111", "alice.jpg", "US");
73+
when(getUserByIdUseCase.execute(42)).thenReturn(user);
74+
75+
ResponseEntity<UserEntity> response = userHandler.getUserById(42);
76+
77+
assertEquals(HttpStatus.OK, response.getStatusCode());
78+
assertEquals(user, response.getBody());
79+
verify(getUserByIdUseCase, times(1)).execute(42);
80+
}
81+
82+
@Test
83+
@DisplayName("Should return 404 when getUserById throws UserNotFoundException")
84+
void shouldReturnNotFoundWhenGetUserByIdFails() {
85+
when(getUserByIdUseCase.execute(99)).thenThrow(new UserNotFoundException(99));
86+
87+
ResponseEntity<UserEntity> response = userHandler.getUserById(99);
88+
89+
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
90+
assertNull(response.getBody());
91+
verify(getUserByIdUseCase, times(1)).execute(99);
92+
}
93+
94+
@Test
95+
@DisplayName("Should return 200 and updated user when updateRandomUser succeeds")
96+
void shouldReturnOkWhenUpdateRandomUserSucceeds() {
97+
UserRequest request = new UserRequest("female", "Jane", "Doe", "Ms", "jane@example.com", "0622222222", "jane.jpg", "FR");
98+
UserEntity updated = new UserEntity(7L, "female", "Jane", "Doe", "Ms", "jane@example.com", "0622222222", "jane.jpg", "FR");
99+
when(updateRandomUserUseCase.execute(7, request)).thenReturn(updated);
100+
101+
ResponseEntity<UserEntity> response = userHandler.updateRandomUser(7, request);
102+
103+
assertEquals(HttpStatus.OK, response.getStatusCode());
104+
assertEquals(updated, response.getBody());
105+
verify(updateRandomUserUseCase, times(1)).execute(7, request);
106+
}
107+
108+
@Test
109+
@DisplayName("Should return 404 when updateRandomUser throws UserNotFoundException")
110+
void shouldReturnNotFoundWhenUpdateRandomUserFails() {
111+
UserRequest request = new UserRequest("male", "Bob", "Brown", "Mr", "bob@example.com", "0633333333", "bob.jpg", "DE");
112+
when(updateRandomUserUseCase.execute(123, request)).thenThrow(new UserNotFoundException(123));
113+
114+
ResponseEntity<UserEntity> response = userHandler.updateRandomUser(123, request);
115+
116+
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
117+
assertNull(response.getBody());
118+
verify(updateRandomUserUseCase, times(1)).execute(123, request);
119+
}
120+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
POSTGRES_USER=your_postgres_user
22
POSTGRES_PASSWORD=your_postgres_password
33
POSTGRES_DB=your_postgres_db
4-
POSTGRES_PORT=5433
4+
POSTGRES_PORT=your_postgres_port
55

66

0 commit comments

Comments
 (0)