diff --git a/src/main/java/org/gridsuite/useradmin/server/controller/UserAdminController.java b/src/main/java/org/gridsuite/useradmin/server/controller/UserAdminController.java index 303008d..389299e 100644 --- a/src/main/java/org/gridsuite/useradmin/server/controller/UserAdminController.java +++ b/src/main/java/org/gridsuite/useradmin/server/controller/UserAdminController.java @@ -107,17 +107,15 @@ public ResponseEntity recordConnectionAttempt(@PathVariable("sub") String @GetMapping(value = "/users/{sub}/profile") @Operation(summary = "Get the user's profile") @ApiResponse(responseCode = "200", description = "The user profile") - @ApiResponse(responseCode = "404", description = "The user doesn't exist") public ResponseEntity getUserProfile(@PathVariable("sub") String sub) { - return ResponseEntity.of(service.getUserProfile(sub)); + return ResponseEntity.ok(service.getUserProfile(sub)); } @GetMapping(value = "/users/{sub}/groups") @Operation(summary = "Get the user's groups") @ApiResponse(responseCode = "200", description = "The user groups") - @ApiResponse(responseCode = "404", description = "The user doesn't exist") public ResponseEntity> getUserGroups(@PathVariable("sub") String sub) { - return ResponseEntity.of(service.getUserGroups(sub)); + return ResponseEntity.ok(service.getUserGroups(sub)); } @GetMapping(value = "/users/{sub}/profile/max-cases") diff --git a/src/main/java/org/gridsuite/useradmin/server/controller/UserInfoController.java b/src/main/java/org/gridsuite/useradmin/server/controller/UserInfoController.java index 67d9951..11b4717 100644 --- a/src/main/java/org/gridsuite/useradmin/server/controller/UserInfoController.java +++ b/src/main/java/org/gridsuite/useradmin/server/controller/UserInfoController.java @@ -33,9 +33,8 @@ public UserInfoController(UserInfosService userInfosService) { @GetMapping(value = "/{sub}/detail", produces = "application/json") @Operation(summary = "get detailed user information") @ApiResponse(responseCode = "200", description = "The user exist") - @ApiResponse(responseCode = "404", description = "The user doesn't exist") public ResponseEntity getUserDetail(@PathVariable("sub") String sub) { - return ResponseEntity.of(userInfosService.getUserInfo(sub)); + return ResponseEntity.ok(userInfosService.getUserInfo(sub)); } } diff --git a/src/main/java/org/gridsuite/useradmin/server/dto/UserProfile.java b/src/main/java/org/gridsuite/useradmin/server/dto/UserProfile.java index ea5354d..9475a39 100644 --- a/src/main/java/org/gridsuite/useradmin/server/dto/UserProfile.java +++ b/src/main/java/org/gridsuite/useradmin/server/dto/UserProfile.java @@ -22,4 +22,24 @@ public record UserProfile( UUID spreadsheetConfigCollectionId, UUID networkVisualizationParameterId, UUID diagramConfigId -) { } +) { + public static final String DEFAULT_PROFILE_NAME = "default profile"; + + public static UserProfile createDefaultProfile(Integer maxAllowedCases, Integer maxAllowedBuilds) { + return new UserProfile( + null, + DEFAULT_PROFILE_NAME, + null, + null, + null, + null, + null, + null, + maxAllowedCases, + maxAllowedBuilds, + null, + null, + null + ); + } +} diff --git a/src/main/java/org/gridsuite/useradmin/server/service/DirectoryService.java b/src/main/java/org/gridsuite/useradmin/server/service/DirectoryService.java index 3cdd520..d78c4cd 100644 --- a/src/main/java/org/gridsuite/useradmin/server/service/DirectoryService.java +++ b/src/main/java/org/gridsuite/useradmin/server/service/DirectoryService.java @@ -6,6 +6,8 @@ */ package org.gridsuite.useradmin.server.service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; @@ -30,6 +32,7 @@ @Service public class DirectoryService { + private static final Logger LOGGER = LoggerFactory.getLogger(DirectoryService.class); private static final String DIRECTORY_SERVER_API_VERSION = "v1"; private static final String DELIMITER = "/"; @@ -73,6 +76,12 @@ public Set getExistingElements(Set elementsUuids, String userId) { public Integer getCasesCount(String userId) { String path = UriComponentsBuilder.fromPath(USER_SERVER_ROOT_PATH + "/{userId}/cases/count").buildAndExpand(userId).toUriString(); - return restTemplate.getForObject(directoryServerBaseUri + path, Integer.class); + try { + return restTemplate.getForObject(directoryServerBaseUri + path, Integer.class); + } catch (Exception e) { + LOGGER.warn("Failed to retrieve cases count for user {}", userId); + return null; + } + } } diff --git a/src/main/java/org/gridsuite/useradmin/server/service/UserAdminService.java b/src/main/java/org/gridsuite/useradmin/server/service/UserAdminService.java index 6a0c0c2..002a421 100644 --- a/src/main/java/org/gridsuite/useradmin/server/service/UserAdminService.java +++ b/src/main/java/org/gridsuite/useradmin/server/service/UserAdminService.java @@ -150,31 +150,54 @@ public Optional getUser(String sub) { } @Transactional(readOnly = true) - public Optional getUserProfile(String sub) { + public UserProfile getUserProfile(String sub) { return doGetUserProfile(sub); } - private Optional doGetUserProfile(String sub) { + private UserProfile doGetUserProfile(String sub) { // this method is not restricted to Admin because it is called by any user to retrieve its own profile - UserInfosEntity user = userInfosRepository.findBySub(sub).orElseThrow(() -> new UserAdminException(NOT_FOUND)); - return user.getProfile() == null ? Optional.empty() : userProfileService.getProfile(user.getProfile().getId()); + Optional userOpt = userInfosRepository.findBySub(sub); + + if (userOpt.isEmpty()) { + return createDefaultProfile(); + } + + UserInfosEntity user = userOpt.get(); + + if (user.getProfile() == null) { + return createDefaultProfile(); + } + + return userProfileService.getProfile(user.getProfile().getId()) + .orElseGet(this::createDefaultProfile); } @Transactional(readOnly = true) - public Optional> getUserGroups(String sub) { - // this method is not restricted to Admin because it is called by any user to retrieve its own profile - UserInfosEntity user = userInfosRepository.findBySub(sub).orElseThrow(() -> new UserAdminException(NOT_FOUND)); - return user.getGroups() == null ? - Optional.empty() : - Optional.of(user.getGroups().stream().map(g -> userGroupService.getGroup(g.getId())) + public List getUserGroups(String sub) { + // this method is not restricted to Admin because it is called by any user to retrieve its own groups + Optional userOpt = userInfosRepository.findBySub(sub); + + if (userOpt.isEmpty()) { + return List.of(); + } + + UserInfosEntity user = userOpt.get(); + + if (user.getGroups() == null || user.getGroups().isEmpty()) { + return List.of(); + } + + return user.getGroups().stream() + .map(g -> userGroupService.getGroup(g.getId())) .filter(Optional::isPresent) - .map(Optional::get).toList()); + .map(Optional::get) + .toList(); } @Transactional(readOnly = true) public Integer getUserProfileMaxAllowedCases(String sub) { - return doGetUserProfile(sub) - .map(UserProfile::maxAllowedCases) + UserProfile profile = doGetUserProfile(sub); + return Optional.ofNullable(profile.maxAllowedCases()) .orElse(applicationProps.getDefaultMaxAllowedCases()); } @@ -184,8 +207,15 @@ public Integer getCasesAlertThreshold() { @Transactional(readOnly = true) public Integer getUserProfileMaxAllowedBuilds(String sub) { - return doGetUserProfile(sub) - .map(UserProfile::maxAllowedBuilds) + UserProfile profile = doGetUserProfile(sub); + return Optional.ofNullable(profile.maxAllowedBuilds()) .orElse(applicationProps.getDefaultMaxAllowedBuilds()); } + + private UserProfile createDefaultProfile() { + return UserProfile.createDefaultProfile( + applicationProps.getDefaultMaxAllowedCases(), + applicationProps.getDefaultMaxAllowedBuilds() + ); + } } diff --git a/src/main/java/org/gridsuite/useradmin/server/service/UserInfosService.java b/src/main/java/org/gridsuite/useradmin/server/service/UserInfosService.java index f27d644..a2095b2 100644 --- a/src/main/java/org/gridsuite/useradmin/server/service/UserInfosService.java +++ b/src/main/java/org/gridsuite/useradmin/server/service/UserInfosService.java @@ -10,6 +10,7 @@ import java.util.Objects; import java.util.Optional; +import java.util.Set; @Service public class UserInfosService { @@ -39,15 +40,25 @@ public UserInfos toDtoUserInfo(final UserInfosEntity userInfosEntity, Integer ca } @Transactional(readOnly = true) - public Optional getUserInfo(String sub) { + public UserInfos getUserInfo(String sub) { Optional userInfosEntity = getUserInfosEntity(sub); + // get number of cases used + Integer casesUsed = directoryService.getCasesCount(sub); if (userInfosEntity.isPresent()) { - // get number of cases used - Integer casesUsed = directoryService.getCasesCount(userInfosEntity.get().getSub()); - - return Optional.of(toDtoUserInfo(userInfosEntity.get(), casesUsed)); + return toDtoUserInfo(userInfosEntity.get(), casesUsed); } - return Optional.empty(); + return createDefaultUserInfo(sub, casesUsed); + } + + private UserInfos createDefaultUserInfo(String sub, Integer casesUsed) { + return new UserInfos( + sub, + null, + applicationProps.getDefaultMaxAllowedCases(), + casesUsed, + applicationProps.getDefaultMaxAllowedBuilds(), + Set.of() + ); } private Optional getUserInfosEntity(String sub) { diff --git a/src/main/resources/config/application.yaml b/src/main/resources/config/application.yaml index eee837c..9715cb2 100644 --- a/src/main/resources/config/application.yaml +++ b/src/main/resources/config/application.yaml @@ -18,6 +18,6 @@ useradmin: cron: announcement-check: 0 */1 * * * * announcement-clean: 0 0 2 * * ? -# defaultMaxAllowedCases: 20 # Default allowed cases for a user (set here for testing purposes) -# defaultMaxAllowedBuilds: 10 # Default allowed builds for a user (set here for testing purposes) -# casesAlertThreshold: 90 # Default usage threshold (percentage) when user gets a warning when uploading cases (set here for testing purposes) + defaultMaxAllowedCases: 20 # Default allowed cases for a user + defaultMaxAllowedBuilds: 20 # Default allowed builds for a user + casesAlertThreshold: 90 # Default usage threshold (percentage) when user gets a warning when uploading cases diff --git a/src/test/java/org/gridsuite/useradmin/server/UserAdminTest.java b/src/test/java/org/gridsuite/useradmin/server/UserAdminTest.java index 9ad8935..f6b8359 100644 --- a/src/test/java/org/gridsuite/useradmin/server/UserAdminTest.java +++ b/src/test/java/org/gridsuite/useradmin/server/UserAdminTest.java @@ -67,6 +67,9 @@ class UserAdminTest { @Autowired private ConnectionRepository connectionRepository; + @Autowired + private UserAdminApplicationProps userAdminApplicationProps; + @AfterEach void cleanDB() { userGroupRepository.deleteAll(); @@ -251,7 +254,21 @@ void testUpdateUserForbidden() throws Exception { @Test void testGetUserProfileNotFound() throws Exception { - getUserProfile("BadUser", HttpStatus.NOT_FOUND); + UserProfile profile = getUserProfile("BadUser", HttpStatus.OK); + assertNotNull(profile); + assertEquals(UserProfile.DEFAULT_PROFILE_NAME, profile.name()); + assertNull(profile.id()); + assertEquals(userAdminApplicationProps.getDefaultMaxAllowedCases(), profile.maxAllowedCases()); + assertEquals(userAdminApplicationProps.getDefaultMaxAllowedBuilds(), profile.maxAllowedBuilds()); + assertNull(profile.loadFlowParameterId()); + assertNull(profile.securityAnalysisParameterId()); + assertNull(profile.sensitivityAnalysisParameterId()); + assertNull(profile.shortcircuitParameterId()); + assertNull(profile.voltageInitParameterId()); + assertNull(profile.allLinksValid()); + assertNull(profile.spreadsheetConfigCollectionId()); + assertNull(profile.networkVisualizationParameterId()); + assertNull(profile.diagramConfigId()); } @Test diff --git a/src/test/java/org/gridsuite/useradmin/server/controller/UserGroupControllerTest.java b/src/test/java/org/gridsuite/useradmin/server/controller/UserGroupControllerTest.java index 2257e0f..1afc22e 100644 --- a/src/test/java/org/gridsuite/useradmin/server/controller/UserGroupControllerTest.java +++ b/src/test/java/org/gridsuite/useradmin/server/controller/UserGroupControllerTest.java @@ -130,7 +130,7 @@ private UserInfos getUserInfos(String userName) throws Exception { new TypeReference<>() { }); } - private Set getUserGroups(String userName) throws Exception { + private List getUserGroups(String userName) throws Exception { return objectMapper.readValue( mockMvc.perform(get(API_BASE_PATH + "/users/" + userName + "/groups") .contentType(APPLICATION_JSON)) @@ -212,7 +212,7 @@ void testGroups() throws Exception { checkUserGroup(USER_B, null); checkUserGroup(USER_C, null); - Set userGroups = getUserGroups(USER_E); + List userGroups = getUserGroups(USER_E); assertEquals(Set.of(GROUP_NEW_NAME), userGroups.stream().map(UserGroup::name).collect(Collectors.toSet())); // delete group : error because of users still referencing it @@ -233,4 +233,11 @@ void testGroups() throws Exception { checkUserGroup(USER_D, null); checkUserGroup(USER_E, null); } + + @Test + void testGetGroupsForNonExistentUser() throws Exception { + List groups = getUserGroups("nonExistentUser"); + assertNotNull(groups); + assertTrue(groups.isEmpty()); + } } diff --git a/src/test/java/org/gridsuite/useradmin/server/service/UserInfosServiceTest.java b/src/test/java/org/gridsuite/useradmin/server/service/UserInfosServiceTest.java index c248cb4..a4900f1 100644 --- a/src/test/java/org/gridsuite/useradmin/server/service/UserInfosServiceTest.java +++ b/src/test/java/org/gridsuite/useradmin/server/service/UserInfosServiceTest.java @@ -21,8 +21,7 @@ import java.util.Optional; import java.util.UUID; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.when; /** @@ -62,12 +61,28 @@ void toDtoUserInfoTest() { UserInfosEntity user = new UserInfosEntity(UUID.randomUUID(), "user_A", profile, null); when(userInfosRepositoryMock.findBySub("user_A")).thenReturn(Optional.of(user)); - Optional userInfos = userInfosService.getUserInfo("user_A"); - assertTrue(userInfos.isPresent()); - assertEquals("user_A", userInfos.get().sub()); - assertEquals("profile_A", userInfos.get().profileName()); - assertEquals(5, userInfos.get().maxAllowedCases()); - assertEquals(3, userInfos.get().numberCasesUsed()); - assertEquals(6, userInfos.get().maxAllowedBuilds()); + UserInfos userInfos = userInfosService.getUserInfo("user_A"); + assertNotNull(userInfos); + assertEquals("user_A", userInfos.sub()); + assertEquals("profile_A", userInfos.profileName()); + assertEquals(5, userInfos.maxAllowedCases()); + assertEquals(3, userInfos.numberCasesUsed()); + assertEquals(6, userInfos.maxAllowedBuilds()); + } + + @Test + void getUserInfoForNonExistentUser() { + when(applicationPropsMock.getDefaultMaxAllowedCases()).thenReturn(20); + when(applicationPropsMock.getDefaultMaxAllowedBuilds()).thenReturn(10); + when(userInfosRepositoryMock.findBySub("nonExistent")).thenReturn(Optional.empty()); + + UserInfos userInfos = userInfosService.getUserInfo("nonExistent"); + assertNotNull(userInfos); + assertEquals("nonExistent", userInfos.sub()); + assertNull(userInfos.profileName()); + assertEquals(20, userInfos.maxAllowedCases()); + assertEquals(0, userInfos.numberCasesUsed()); + assertEquals(10, userInfos.maxAllowedBuilds()); + assertTrue(userInfos.groups().isEmpty()); } }