Skip to content

Commit 4bab8d0

Browse files
hoangnt2GMishx
authored andcommitted
feat(User): Add 2 new endpoints:
- Allow Admin user to update user - List all existing department Signed-off-by: hoangnt2 <[email protected]>
1 parent c9dce5a commit 4bab8d0

File tree

9 files changed

+156
-13
lines changed

9 files changed

+156
-13
lines changed

backend/common/src/main/java/org/eclipse/sw360/datahandler/db/UserRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ public Set<String> getUserDepartments() {
177177
return getResultBasedOnQuery("userDepartments");
178178
}
179179

180+
public Set<String> getUserSecondaryDepartments() {
181+
return getResultBasedOnQuery("userSecondaryDepartments");
182+
}
183+
180184
public Set<String> getUserEmails() {
181185
return getResultBasedOnQuery("userEmails");
182186
}

backend/users/src/main/java/org/eclipse/sw360/users/UserHandler.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.eclipse.sw360.datahandler.thrift.PaginationData;
3232
import org.eclipse.sw360.datahandler.thrift.RequestStatus;
3333
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
34+
import org.eclipse.sw360.datahandler.thrift.SW360Exception;
3435
import org.eclipse.sw360.datahandler.thrift.users.DepartmentConfigDTO;
3536
import org.eclipse.sw360.datahandler.thrift.users.User;
3637
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
@@ -95,8 +96,10 @@ public UserHandler(Cloudant client, String userDbName) throws IOException {
9596
}
9697

9798
@Override
98-
public User getUser(String id) {
99-
return db.getUser(id);
99+
public User getUser(String id) throws SW360Exception {
100+
User user = db.getUser(id);
101+
assertNotNull(user);
102+
return user;
100103
}
101104

102105
@Override
@@ -337,4 +340,9 @@ public List<User> searchDepartmentUsers(String department) throws TException {
337340
public List<User> searchUsersGroup(UserGroup userGroup) throws TException {
338341
return db.getAllUsersGroup(userGroup);
339342
}
343+
344+
@Override
345+
public Set<String> getUserSecondaryDepartments() throws TException {
346+
return db.getUserSecondaryDepartments();
347+
}
340348
}

backend/users/src/main/java/org/eclipse/sw360/users/db/UserDatabaseHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,4 +468,8 @@ public List<User> getAllUsersGroup(UserGroup userGroup) {
468468
}
469469
return userGroups;
470470
}
471+
472+
public Set<String> getUserSecondaryDepartments() {
473+
return repository.getUserSecondaryDepartments();
474+
}
471475
}

libraries/datahandler/src/main/thrift/users.thrift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ typedef sw360.AddDocumentRequestSummary AddDocumentRequestSummary
1616
typedef sw360.RequestStatus RequestStatus
1717
typedef sw360.RequestSummary RequestSummary
1818
typedef sw360.PaginationData PaginationData
19+
typedef sw360.SW360Exception SW360Exception
1920

2021
enum UserGroup {
2122
USER = 0,
@@ -98,7 +99,7 @@ service UserService {
9899
/**
99100
* returns SW360-user with given id
100101
**/
101-
User getUser(1:string id);
102+
User getUser(1:string id) throws (1: SW360Exception exp);
102103

103104
/**
104105
* returns SW360-user with given email
@@ -210,4 +211,5 @@ service UserService {
210211

211212
set<string> getAllEmailsByDepartmentKey(1: string departmentName)
212213

214+
set<string> getUserSecondaryDepartments()
213215
}

rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/core/RestControllerHelper.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,16 @@ public User updateUserProfile(User userToUpdate, Map<String, Object> requestBody
740740
return userToUpdate;
741741
}
742742

743+
public User updateUser(User userToUpdate, User requestBodyUser) {
744+
for (User._Fields field:User._Fields.values()) {
745+
Object fieldValue = requestBodyUser.getFieldValue(field);
746+
if (fieldValue != null) {
747+
userToUpdate.setFieldValue(field, fieldValue);
748+
}
749+
}
750+
return userToUpdate;
751+
}
752+
743753
public Component convertToComponent(ComponentDTO componentDTO) {
744754
Component component = new Component();
745755

@@ -1051,6 +1061,7 @@ public User convertToEmbeddedGetUsers(User user) {
10511061
embeddedUser.setDepartment(user.getDepartment());
10521062
embeddedUser.setUserGroup(user.getUserGroup());
10531063
embeddedUser.setSecondaryDepartmentsAndRoles(user.getSecondaryDepartmentsAndRoles());
1064+
embeddedUser.setDeactivated(user.isDeactivated());
10541065
embeddedUser.setType(null);
10551066
return embeddedUser;
10561067
}

rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/user/Sw360UserService.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
import com.fasterxml.jackson.databind.DeserializationFeature;
1414
import com.fasterxml.jackson.databind.ObjectMapper;
15+
import com.google.common.collect.Sets;
16+
import org.apache.logging.log4j.LogManager;
17+
import org.apache.logging.log4j.Logger;
1518
import org.apache.thrift.TException;
1619
import org.apache.thrift.protocol.TCompactProtocol;
1720
import org.apache.thrift.protocol.TProtocol;
@@ -29,15 +32,16 @@
2932
import org.eclipse.sw360.datahandler.thrift.users.UserService;
3033
import org.springframework.beans.factory.annotation.Value;
3134
import org.springframework.dao.DataIntegrityViolationException;
35+
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
3236
import org.springframework.http.converter.HttpMessageNotReadableException;
3337
import org.springframework.stereotype.Service;
3438

35-
import java.util.Collection;
3639
import java.time.LocalDate;
3740
import java.time.temporal.ChronoUnit;
3841
import java.util.List;
3942
import java.util.Set;
4043
import java.util.Map;
44+
import java.util.Collections;
4145
import java.util.stream.Collectors;
4246

4347
import static org.eclipse.sw360.rest.resourceserver.Sw360ResourceServer.API_TOKEN_MAX_VALIDITY_READ_IN_DAYS;
@@ -46,6 +50,7 @@
4650

4751
@Service
4852
public class Sw360UserService {
53+
private static final Logger log = LogManager.getLogger(Sw360UserService.class);
4954
@Value("${sw360.thrift-server-url:http://localhost:8080}")
5055
private String thriftServerUrl;
5156
private static final String AUTHORITIES_READ = "READ";
@@ -79,12 +84,16 @@ public User getUserByEmailOrExternalId(String userIdentifier) {
7984
}
8085
}
8186

82-
public User getUser(String id) {
87+
public User getUser(String id) throws TException {
88+
UserService.Iface sw360UserClient = getThriftUserClient();
8389
try {
84-
UserService.Iface sw360UserClient = getThriftUserClient();
8590
return sw360UserClient.getUser(id);
86-
} catch (TException e) {
87-
throw new RuntimeException(e);
91+
} catch (SW360Exception sw360Exp) {
92+
if (sw360Exp.getErrorCode() == 404) {
93+
throw new ResourceNotFoundException("Requested User Not Found");
94+
} else {
95+
throw sw360Exp;
96+
}
8897
}
8998
}
9099

@@ -259,4 +268,30 @@ private void validateRestApiToken(RestApiToken restApiToken, User sw360User) {
259268
throw new IllegalArgumentException("Invalid permissions: " + String.join(", ", otherPermissions) + ".");
260269
}
261270
}
271+
272+
public Set<String> getAvailableDepartments() {
273+
Set<String> primaryDepartments = getExistingPrimaryDepartments();
274+
Set<String> secondaryDepartments = getExistingSecondaryDepartments();
275+
return Sets.union(primaryDepartments, secondaryDepartments);
276+
}
277+
278+
public Set<String> getExistingPrimaryDepartments() {
279+
try {
280+
UserService.Iface sw360UserClient = getThriftUserClient();
281+
return sw360UserClient.getUserDepartments();
282+
} catch (TException e) {
283+
log.error(e.getMessage());
284+
return Collections.emptySet();
285+
}
286+
}
287+
288+
public Set<String> getExistingSecondaryDepartments() {
289+
try {
290+
UserService.Iface sw360UserClient = getThriftUserClient();
291+
return sw360UserClient.getUserSecondaryDepartments();
292+
} catch (TException e) {
293+
log.error(e.getMessage());
294+
return Collections.emptySet();
295+
}
296+
}
262297
}

rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/user/UserController.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
package org.eclipse.sw360.rest.resourceserver.user;
1010

11+
import com.google.common.collect.ImmutableMap;
1112
import com.google.common.collect.ImmutableSet;
1213
import io.swagger.v3.oas.annotations.Parameter;
1314
import io.swagger.v3.oas.annotations.media.Content;
@@ -35,6 +36,7 @@
3536
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
3637
import org.eclipse.sw360.rest.resourceserver.core.HalResource;
3738
import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper;
39+
import org.jetbrains.annotations.NotNull;
3840
import org.springframework.beans.factory.annotation.Autowired;
3941
import org.springframework.data.rest.webmvc.BasePathAwareController;
4042
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
@@ -45,6 +47,7 @@
4547
import org.springframework.http.HttpStatus;
4648
import org.springframework.http.ResponseEntity;
4749
import org.springframework.http.converter.HttpMessageNotReadableException;
50+
import org.springframework.security.access.prepost.PreAuthorize;
4851
import org.springframework.security.crypto.bcrypt.BCrypt;
4952
import org.springframework.security.crypto.password.PasswordEncoder;
5053
import org.springframework.web.bind.annotation.*;
@@ -208,7 +211,7 @@ public ResponseEntity<EntityModel<User>> getUserByEmail(@Parameter(
208211
tags = {"Users"})
209212
@RequestMapping(value = USERS_URL + "/byid/{id:.+}", method = RequestMethod.GET)
210213
public ResponseEntity<EntityModel<User>> getUser(@Parameter(
211-
description = "The id of the user to be retrieved.") @PathVariable("id") String id) {
214+
description = "The id of the user to be retrieved.") @PathVariable("id") String id) throws TException {
212215
User sw360User = userService.getUser(id);
213216
HalResource<User> halResource = createHalUser(sw360User);
214217
return new ResponseEntity<>(halResource, HttpStatus.OK);
@@ -384,4 +387,45 @@ public ResponseEntity<Map<String, List<String>>> getGroupList() {
384387
userGroupMap.put("secondaryGrpList", secondaryGrpList);
385388
return new ResponseEntity<>(userGroupMap, HttpStatus.OK);
386389
}
390+
391+
@Operation(summary = "Update an existing user.", description = "Update an existing user",
392+
tags = {"Users"})
393+
@PatchMapping(value = USERS_URL + "/{id}")
394+
@PreAuthorize("hasAuthority('ADMIN')")
395+
public ResponseEntity<EntityModel<User>> patchUser(
396+
@Parameter(description = "The user data to be updated.", schema = @Schema(implementation = User.class)) @RequestBody @NotNull User user,
397+
@Parameter(description = "Id of updated user") @PathVariable String id
398+
) throws TException {
399+
if (user.getPassword() != null && user.getPassword().isEmpty()) {
400+
user.unsetPassword();
401+
}
402+
if (user.getPassword() != null) {
403+
String encodedPassword = passwordEncoder.encode(user.getPassword());
404+
user.setPassword(encodedPassword);
405+
}
406+
407+
User userToUpdate = userService.getUser(id);
408+
userToUpdate = this.restControllerHelper.updateUser(userToUpdate, user);
409+
410+
userService.updateUser(userToUpdate);
411+
HalResource<User> halResource = createHalUser(userToUpdate);
412+
413+
return new ResponseEntity<>(halResource, HttpStatus.OK);
414+
}
415+
416+
@Operation(summary = "Get existing departments.", description = "Get existing departments from all users",
417+
tags = {"Users"})
418+
@GetMapping(value = USERS_URL + "/departments")
419+
public ResponseEntity<?> getExistingDepartments(
420+
@Parameter(description = "Type of department (primary, secondary)") @RequestParam(value = "type", required = false) String type
421+
) {
422+
if (!CommonUtils.isNotNullEmptyOrWhitespace(type)) {
423+
return new ResponseEntity<>(userService.getAvailableDepartments(), HttpStatus.OK);
424+
}
425+
return switch (type.toLowerCase()) {
426+
case "primary" -> new ResponseEntity<>(userService.getExistingPrimaryDepartments(), HttpStatus.OK);
427+
case "secondary" -> new ResponseEntity<>(userService.getExistingSecondaryDepartments(), HttpStatus.OK);
428+
default -> new ResponseEntity<>("Type must be: primary or secondary", HttpStatus.BAD_REQUEST);
429+
};
430+
}
387431
}

rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/integration/UserTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121

22+
import org.apache.thrift.TException;
2223
import org.eclipse.sw360.datahandler.thrift.users.User;
2324
import org.eclipse.sw360.rest.resourceserver.TestHelper;
2425
import org.eclipse.sw360.rest.resourceserver.user.Sw360UserService;
@@ -45,7 +46,7 @@ public class UserTest extends TestIntegrationBase {
4546
private int port;
4647

4748
@Before
48-
public void before() {
49+
public void before() throws TException {
4950
List<User> userList = new ArrayList<>();
5051

5152
User user = new User();

rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/UserSpecTest.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,10 @@
1616
import org.eclipse.sw360.datahandler.thrift.users.User;
1717
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
1818
import org.eclipse.sw360.rest.resourceserver.TestHelper;
19-
import org.eclipse.sw360.rest.resourceserver.user.Sw360UserService;
2019
import org.junit.Before;
2120
import org.junit.Test;
2221
import org.junit.runner.RunWith;
23-
import org.mockito.Mock;
2422
import org.springframework.beans.factory.annotation.Value;
25-
import org.springframework.boot.test.mock.mockito.MockBean;
2623
import org.springframework.hateoas.MediaTypes;
2724
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
2825

@@ -415,4 +412,41 @@ public void should_document_get_grouplist() throws Exception {
415412
TestHelper.generateAuthHeader(testUserId, testUserPassword)))
416413
.andExpect(status().isOk());
417414
}
415+
416+
@Test
417+
public void should_document_get_all_departments() throws Exception {
418+
mockMvc.perform(get("/api/users/departments")
419+
.contentType(MediaTypes.HAL_JSON)
420+
.header("Authorization",
421+
TestHelper.generateAuthHeader(testUserId, testUserPassword)))
422+
.andExpect(status().isOk());
423+
}
424+
425+
@Test
426+
public void should_document_update_existing_user() throws Exception {
427+
Map<String, String> updateInfo = new HashMap<>();
428+
updateInfo.put("department", "DEPARTMENT");
429+
mockMvc.perform(patch("/api/users/" + user.getId())
430+
.header("Authorization",
431+
TestHelper.generateAuthHeader(testUserId, testUserPassword))
432+
.contentType(MediaTypes.HAL_JSON)
433+
.content(this.objectMapper.writeValueAsString(updateInfo))
434+
.accept(MediaTypes.HAL_JSON))
435+
.andExpect(status().isOk())
436+
.andDo(this.documentationHandler.document(
437+
responseFields(
438+
fieldWithPath("email").description("The user's email"),
439+
fieldWithPath("userGroup").description("The user group, possible values are: " + Arrays.asList(UserGroup.values())),
440+
fieldWithPath("fullName").description("The users's full name"),
441+
fieldWithPath("givenName").description("The user's given name"),
442+
fieldWithPath("lastName").description("The user's last name"),
443+
fieldWithPath("department").description("The user's company department"),
444+
fieldWithPath("deactivated").description("Is user deactivated"),
445+
subsectionWithPath("secondaryDepartmentsAndRoles").description("The user's secondary departments and roles"),
446+
fieldWithPath("formerEmailAddresses").description("The user's former email addresses"),
447+
fieldWithPath("wantsMailNotification").description("Does user want to be notified via mail?"),
448+
subsectionWithPath("notificationPreferences").description("User's notification preferences"),
449+
subsectionWithPath("_links").description("<<resources-index-links,Links>> to other resources")
450+
)));
451+
}
418452
}

0 commit comments

Comments
 (0)