Skip to content

Commit

Permalink
feat(User): Add 2 new endpoints:
Browse files Browse the repository at this point in the history
- Allow Admin user to update user
- List all existing department

Signed-off-by: hoangnt2 <[email protected]>
  • Loading branch information
hoangnt2 authored and GMishx committed Feb 19, 2025
1 parent c9dce5a commit 4bab8d0
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ public Set<String> getUserDepartments() {
return getResultBasedOnQuery("userDepartments");
}

public Set<String> getUserSecondaryDepartments() {
return getResultBasedOnQuery("userSecondaryDepartments");
}

public Set<String> getUserEmails() {
return getResultBasedOnQuery("userEmails");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.eclipse.sw360.datahandler.thrift.PaginationData;
import org.eclipse.sw360.datahandler.thrift.RequestStatus;
import org.eclipse.sw360.datahandler.thrift.RequestSummary;
import org.eclipse.sw360.datahandler.thrift.SW360Exception;
import org.eclipse.sw360.datahandler.thrift.users.DepartmentConfigDTO;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
Expand Down Expand Up @@ -95,8 +96,10 @@ public UserHandler(Cloudant client, String userDbName) throws IOException {
}

@Override
public User getUser(String id) {
return db.getUser(id);
public User getUser(String id) throws SW360Exception {
User user = db.getUser(id);
assertNotNull(user);
return user;
}

@Override
Expand Down Expand Up @@ -337,4 +340,9 @@ public List<User> searchDepartmentUsers(String department) throws TException {
public List<User> searchUsersGroup(UserGroup userGroup) throws TException {
return db.getAllUsersGroup(userGroup);
}

@Override
public Set<String> getUserSecondaryDepartments() throws TException {
return db.getUserSecondaryDepartments();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -468,4 +468,8 @@ public List<User> getAllUsersGroup(UserGroup userGroup) {
}
return userGroups;
}

public Set<String> getUserSecondaryDepartments() {
return repository.getUserSecondaryDepartments();
}
}
4 changes: 3 additions & 1 deletion libraries/datahandler/src/main/thrift/users.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef sw360.AddDocumentRequestSummary AddDocumentRequestSummary
typedef sw360.RequestStatus RequestStatus
typedef sw360.RequestSummary RequestSummary
typedef sw360.PaginationData PaginationData
typedef sw360.SW360Exception SW360Exception

enum UserGroup {
USER = 0,
Expand Down Expand Up @@ -98,7 +99,7 @@ service UserService {
/**
* returns SW360-user with given id
**/
User getUser(1:string id);
User getUser(1:string id) throws (1: SW360Exception exp);

/**
* returns SW360-user with given email
Expand Down Expand Up @@ -210,4 +211,5 @@ service UserService {

set<string> getAllEmailsByDepartmentKey(1: string departmentName)

set<string> getUserSecondaryDepartments()
}
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,16 @@ public User updateUserProfile(User userToUpdate, Map<String, Object> requestBody
return userToUpdate;
}

public User updateUser(User userToUpdate, User requestBodyUser) {
for (User._Fields field:User._Fields.values()) {
Object fieldValue = requestBodyUser.getFieldValue(field);
if (fieldValue != null) {
userToUpdate.setFieldValue(field, fieldValue);
}
}
return userToUpdate;
}

public Component convertToComponent(ComponentDTO componentDTO) {
Component component = new Component();

Expand Down Expand Up @@ -1051,6 +1061,7 @@ public User convertToEmbeddedGetUsers(User user) {
embeddedUser.setDepartment(user.getDepartment());
embeddedUser.setUserGroup(user.getUserGroup());
embeddedUser.setSecondaryDepartmentsAndRoles(user.getSecondaryDepartmentsAndRoles());
embeddedUser.setDeactivated(user.isDeactivated());
embeddedUser.setType(null);
return embeddedUser;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
Expand All @@ -29,15 +32,16 @@
import org.eclipse.sw360.datahandler.thrift.users.UserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Collections;
import java.util.stream.Collectors;

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

@Service
public class Sw360UserService {
private static final Logger log = LogManager.getLogger(Sw360UserService.class);
@Value("${sw360.thrift-server-url:http://localhost:8080}")
private String thriftServerUrl;
private static final String AUTHORITIES_READ = "READ";
Expand Down Expand Up @@ -79,12 +84,16 @@ public User getUserByEmailOrExternalId(String userIdentifier) {
}
}

public User getUser(String id) {
public User getUser(String id) throws TException {
UserService.Iface sw360UserClient = getThriftUserClient();
try {
UserService.Iface sw360UserClient = getThriftUserClient();
return sw360UserClient.getUser(id);
} catch (TException e) {
throw new RuntimeException(e);
} catch (SW360Exception sw360Exp) {
if (sw360Exp.getErrorCode() == 404) {
throw new ResourceNotFoundException("Requested User Not Found");
} else {
throw sw360Exp;
}
}
}

Expand Down Expand Up @@ -259,4 +268,30 @@ private void validateRestApiToken(RestApiToken restApiToken, User sw360User) {
throw new IllegalArgumentException("Invalid permissions: " + String.join(", ", otherPermissions) + ".");
}
}

public Set<String> getAvailableDepartments() {
Set<String> primaryDepartments = getExistingPrimaryDepartments();
Set<String> secondaryDepartments = getExistingSecondaryDepartments();
return Sets.union(primaryDepartments, secondaryDepartments);
}

public Set<String> getExistingPrimaryDepartments() {
try {
UserService.Iface sw360UserClient = getThriftUserClient();
return sw360UserClient.getUserDepartments();
} catch (TException e) {
log.error(e.getMessage());
return Collections.emptySet();
}
}

public Set<String> getExistingSecondaryDepartments() {
try {
UserService.Iface sw360UserClient = getThriftUserClient();
return sw360UserClient.getUserSecondaryDepartments();
} catch (TException e) {
log.error(e.getMessage());
return Collections.emptySet();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/
package org.eclipse.sw360.rest.resourceserver.user;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
Expand Down Expand Up @@ -35,6 +36,7 @@
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
import org.eclipse.sw360.rest.resourceserver.core.HalResource;
import org.eclipse.sw360.rest.resourceserver.core.RestControllerHelper;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
Expand All @@ -45,6 +47,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -208,7 +211,7 @@ public ResponseEntity<EntityModel<User>> getUserByEmail(@Parameter(
tags = {"Users"})
@RequestMapping(value = USERS_URL + "/byid/{id:.+}", method = RequestMethod.GET)
public ResponseEntity<EntityModel<User>> getUser(@Parameter(
description = "The id of the user to be retrieved.") @PathVariable("id") String id) {
description = "The id of the user to be retrieved.") @PathVariable("id") String id) throws TException {
User sw360User = userService.getUser(id);
HalResource<User> halResource = createHalUser(sw360User);
return new ResponseEntity<>(halResource, HttpStatus.OK);
Expand Down Expand Up @@ -384,4 +387,45 @@ public ResponseEntity<Map<String, List<String>>> getGroupList() {
userGroupMap.put("secondaryGrpList", secondaryGrpList);
return new ResponseEntity<>(userGroupMap, HttpStatus.OK);
}

@Operation(summary = "Update an existing user.", description = "Update an existing user",
tags = {"Users"})
@PatchMapping(value = USERS_URL + "/{id}")
@PreAuthorize("hasAuthority('ADMIN')")
public ResponseEntity<EntityModel<User>> patchUser(
@Parameter(description = "The user data to be updated.", schema = @Schema(implementation = User.class)) @RequestBody @NotNull User user,
@Parameter(description = "Id of updated user") @PathVariable String id
) throws TException {
if (user.getPassword() != null && user.getPassword().isEmpty()) {
user.unsetPassword();
}
if (user.getPassword() != null) {
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
}

User userToUpdate = userService.getUser(id);
userToUpdate = this.restControllerHelper.updateUser(userToUpdate, user);

userService.updateUser(userToUpdate);
HalResource<User> halResource = createHalUser(userToUpdate);

return new ResponseEntity<>(halResource, HttpStatus.OK);
}

@Operation(summary = "Get existing departments.", description = "Get existing departments from all users",
tags = {"Users"})
@GetMapping(value = USERS_URL + "/departments")
public ResponseEntity<?> getExistingDepartments(
@Parameter(description = "Type of department (primary, secondary)") @RequestParam(value = "type", required = false) String type
) {
if (!CommonUtils.isNotNullEmptyOrWhitespace(type)) {
return new ResponseEntity<>(userService.getAvailableDepartments(), HttpStatus.OK);
}
return switch (type.toLowerCase()) {
case "primary" -> new ResponseEntity<>(userService.getExistingPrimaryDepartments(), HttpStatus.OK);
case "secondary" -> new ResponseEntity<>(userService.getExistingSecondaryDepartments(), HttpStatus.OK);
default -> new ResponseEntity<>("Type must be: primary or secondary", HttpStatus.BAD_REQUEST);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.List;

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

@Before
public void before() {
public void before() throws TException {
List<User> userList = new ArrayList<>();

User user = new User();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.datahandler.thrift.users.UserGroup;
import org.eclipse.sw360.rest.resourceserver.TestHelper;
import org.eclipse.sw360.rest.resourceserver.user.Sw360UserService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.hateoas.MediaTypes;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

Expand Down Expand Up @@ -415,4 +412,41 @@ public void should_document_get_grouplist() throws Exception {
TestHelper.generateAuthHeader(testUserId, testUserPassword)))
.andExpect(status().isOk());
}

@Test
public void should_document_get_all_departments() throws Exception {
mockMvc.perform(get("/api/users/departments")
.contentType(MediaTypes.HAL_JSON)
.header("Authorization",
TestHelper.generateAuthHeader(testUserId, testUserPassword)))
.andExpect(status().isOk());
}

@Test
public void should_document_update_existing_user() throws Exception {
Map<String, String> updateInfo = new HashMap<>();
updateInfo.put("department", "DEPARTMENT");
mockMvc.perform(patch("/api/users/" + user.getId())
.header("Authorization",
TestHelper.generateAuthHeader(testUserId, testUserPassword))
.contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(updateInfo))
.accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
responseFields(
fieldWithPath("email").description("The user's email"),
fieldWithPath("userGroup").description("The user group, possible values are: " + Arrays.asList(UserGroup.values())),
fieldWithPath("fullName").description("The users's full name"),
fieldWithPath("givenName").description("The user's given name"),
fieldWithPath("lastName").description("The user's last name"),
fieldWithPath("department").description("The user's company department"),
fieldWithPath("deactivated").description("Is user deactivated"),
subsectionWithPath("secondaryDepartmentsAndRoles").description("The user's secondary departments and roles"),
fieldWithPath("formerEmailAddresses").description("The user's former email addresses"),
fieldWithPath("wantsMailNotification").description("Does user want to be notified via mail?"),
subsectionWithPath("notificationPreferences").description("User's notification preferences"),
subsectionWithPath("_links").description("<<resources-index-links,Links>> to other resources")
)));
}
}

0 comments on commit 4bab8d0

Please sign in to comment.