Commit 43f36407 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

user serialization done via default serializer

parent b6435325
package lcsb.mapviewer.model.user;
import java.awt.*;
import java.awt.Color;
import java.io.Serializable;
import java.util.*;
import javax.persistence.*;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Type;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lcsb.mapviewer.model.security.Privilege;
@Entity
......@@ -24,6 +41,7 @@ public class User implements Serializable {
private String login;
@Column(nullable = false, length = 255)
@JsonIgnore
private String cryptedPassword;
@Column(length = 255)
......@@ -77,6 +95,7 @@ public class User implements Serializable {
@CollectionTable(name = "user_terms_of_use_consent", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "date")
@Cascade({ org.hibernate.annotations.CascadeType.ALL })
@JsonIgnore
private Set<Calendar> termsOfUseConsentDates = new HashSet<>();
@ManyToMany(cascade = CascadeType.ALL)
......@@ -85,6 +104,7 @@ public class User implements Serializable {
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn
@JsonProperty("preferences")
private UserAnnotationSchema annotationSchema;
public User() {
......@@ -168,7 +188,7 @@ public class User implements Serializable {
public void setAnnotationSchema(UserAnnotationSchema annotationSchema) {
this.annotationSchema = annotationSchema;
if (!this.equals(annotationSchema.getUser())) {
if (annotationSchema!=null && !this.equals(annotationSchema.getUser())) {
annotationSchema.setUser(this);
}
}
......
......@@ -78,7 +78,7 @@ public class UserController extends BaseController {
@PreAuthorize("hasAnyAuthority('IS_ADMIN', 'IS_CURATOR') or #login == authentication.name")
@GetMapping(value = "/users/{login:.+}")
public Map<String, Object> getUser(
public UserDTO getUser(
@PathVariable(value = "login") String login,
@RequestParam(value = "columns", defaultValue = "") String columns) throws ObjectNotFoundException {
return userRest.getUser(login, columns);
......@@ -86,7 +86,7 @@ public class UserController extends BaseController {
@PreAuthorize("hasAuthority('IS_ADMIN')")
@PatchMapping(value = "/users/{login}:updatePrivileges")
public Map<String, Object> updatePrivileges(
public UserDTO updatePrivileges(
@RequestBody String body,
@PathVariable(value = "login") String login) throws IOException, QueryException {
Map<String, Object> node = parseBody(body);
......@@ -96,7 +96,7 @@ public class UserController extends BaseController {
@PreAuthorize("hasAuthority('IS_ADMIN') or #login == authentication.name")
@PatchMapping(value = "/users/{login}:updatePreferences")
public Map<String, Object> updatePreferences(
public UserPreferencesDTO updatePreferences(
@RequestBody String body,
@PathVariable(value = "login") String login) throws IOException, QueryException {
Map<String, Object> node = parseBody(body);
......@@ -106,15 +106,15 @@ public class UserController extends BaseController {
@PreAuthorize("hasAnyAuthority('IS_ADMIN', 'IS_CURATOR')")
@GetMapping(value = "/users/")
public List<Map<String, Object>> getUsers(@RequestParam(value = "columns", defaultValue = "") String columns) {
public List<UserDTO> getUsers(@RequestParam(value = "columns", defaultValue = "") String columns) {
return userRest.getUsers(columns).stream()
.sorted(Comparator.comparing(user -> (String) user.get("login"), Comparator.reverseOrder()))
.sorted(Comparator.comparing(user -> user.getLogin(), Comparator.reverseOrder()))
.collect(Collectors.toList());
}
@PreAuthorize("hasAuthority('IS_ADMIN') or #login == authentication.name")
@PatchMapping(value = "/users/{login:.+}")
public Map<String, Object> updateUser(
public UserDTO updateUser(
@RequestBody String body,
@PathVariable(value = "login") String login,
Authentication authentication) throws QueryException, IOException {
......@@ -125,11 +125,10 @@ public class UserController extends BaseController {
@PreAuthorize("hasAuthority('IS_ADMIN')")
@PostMapping(value = "/users/{login:.+}")
public Map<String, Object> addUser(
public UserDTO addUser(
@RequestBody MultiValueMap<String, Object> formData,
@PathVariable(value = "login") String login) throws QueryException {
Map<String, Object> userMap = userRest.addUser(login, formData);
return userMap;
return userRest.addUser(login, formData);
}
@PreAuthorize("hasAuthority('IS_ADMIN')")
......
package lcsb.mapviewer.api.users;
import lcsb.mapviewer.model.user.User;
public class UserDTO extends User {
/**
*
*/
private static final long serialVersionUID = 1L;
public boolean ldapAccountAvailable;
public UserDTO(User user, boolean ldapAccountAvailable) {
this.ldapAccountAvailable = ldapAccountAvailable;
setId(user.getId());
setLogin(user.getLogin());
setCryptedPassword(user.getCryptedPassword());
setName(user.getName());
setSurname(user.getSurname());
setEmail(user.getEmail());
setMinColor(user.getMinColor());
setMaxColor(user.getMaxColor());
setNeutralColor(user.getNeutralColor());
setSimpleColor(user.getSimpleColor());
setRemoved(user.isRemoved());
setConnectedToLdap(user.isConnectedToLdap());
setTermsOfUseConsent(user.isTermsOfUseConsent());
setTermsOfUseConsentDates(user.getTermsOfUseConsentDates());
setPrivileges(user.getPrivileges());
setAnnotationSchema(user.getAnnotationSchema());
}
}
package lcsb.mapviewer.api.users;
import lcsb.mapviewer.model.user.UserAnnotationSchema;
public class UserPreferencesDTO {
public UserPreferencesDTO(UserAnnotationSchema annotationSchema) {
this.preferences = annotationSchema;
}
public UserAnnotationSchema preferences;
}
......@@ -9,7 +9,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -62,7 +61,7 @@ public class UserRestImpl extends BaseRestImpl {
this.passwordEncoder = passwordEncoder;
}
public Map<String, Object> getUser(String login, String columns)
public UserDTO getUser(String login, String columns)
throws ObjectNotFoundException {
Set<String> columnSet = createUserColumnSet(columns);
User user = getUserService().getUserByLogin(login, true);
......@@ -75,7 +74,7 @@ public class UserRestImpl extends BaseRestImpl {
userList.add(user);
ldapAvailable = getUserService().ldapAccountExistsForLogin(userList).get(login);
}
return prepareUse(user, columnSet, ldapAvailable);
return new UserDTO(user, ldapAvailable == true);
}
public Set<String> createUserColumnSet(String columns) {
......@@ -101,70 +100,6 @@ public class UserRestImpl extends BaseRestImpl {
return columnsSet;
}
private Map<String, Object> prepareUse(User user, Set<String> columnsSet, Boolean ldapAvailable) {
Map<String, Object> result = new TreeMap<>();
for (String string : columnsSet) {
String column = string.toLowerCase();
Object value;
switch (column) {
case "id":
case "idobject":
value = user.getId();
break;
case "name":
value = user.getName();
break;
case "surname":
value = user.getSurname();
break;
case "login":
value = user.getLogin();
break;
case "email":
value = user.getEmail();
break;
case "mincolor":
value = user.getMinColor();
break;
case "maxcolor":
value = user.getMaxColor();
break;
case "neutralcolor":
value = user.getNeutralColor();
break;
case "simplecolor":
value = user.getSimpleColor();
break;
case "removed":
value = user.isRemoved();
break;
case "termsofuseconsent":
value = user.isTermsOfUseConsent();
break;
case "connectedtoldap":
value = user.isConnectedToLdap();
break;
case "ldapaccountavailable":
if (ldapAvailable == null) {
ldapAvailable = false;
}
value = ldapAvailable;
break;
case "privileges":
value = user.getPrivileges();
break;
case "preferences":
value = user.getAnnotationSchema();
break;
default:
value = "Unknown column";
break;
}
result.put(string, value);
}
return result;
}
private void updateValidAnnotations(UserAnnotationSchema schema, Map<String, Object> data) {
for (String key : data.keySet()) {
UserClassValidAnnotations annotator = null;
......@@ -430,29 +365,17 @@ public class UserRestImpl extends BaseRestImpl {
}
}
private Map<String, Object> prepareProjectUploadPreferences(UserAnnotationSchema schema) {
Map<String, Object> result = new TreeMap<>();
result.put("validate-miriam", schema.getValidateMiriamTypes());
result.put("annotate-model", schema.getAnnotateModel());
result.put("cache-data", schema.getCacheData());
result.put("auto-resize", schema.getAutoResizeMap());
result.put("semantic-zooming-contains-multiple-overlays", schema.getSemanticZoomContainsMultipleOverlays());
result.put("sbgn", schema.getSbgnFormat());
return result;
}
public List<Map<String, Object>> getUsers(String columns) {
Set<String> columnSet = createUserColumnSet(columns);
List<Map<String, Object>> result = new ArrayList<>();
public List<UserDTO> getUsers(String columns) {
List<UserDTO> result = new ArrayList<>();
List<User> users = getUserService().getUsers(true);
Map<String, Boolean> ldapAvailability = getUserService().ldapAccountExistsForLogin(users);
for (User user : users) {
result.add(prepareUse(user, columnSet, ldapAvailability.get(user.getLogin())));
result.add(new UserDTO(user, ldapAvailability.get(user.getLogin()) == true));
}
return result;
}
public Map<String, Object> updatePrivileges(String login, Map<String, Object> privilegesData) throws QueryException {
public UserDTO updatePrivileges(String login, Map<String, Object> privilegesData) throws QueryException {
if (privilegesData == null) {
throw new QueryException("Privileges not defined");
}
......@@ -501,7 +424,7 @@ public class UserRestImpl extends BaseRestImpl {
return getUser(login, "");
}
public Map<String, Object> updatePreferences(String login, Map<String, Object> preferencesData)
public UserPreferencesDTO updatePreferences(String login, Map<String, Object> preferencesData)
throws QueryException {
if (preferencesData == null) {
throw new QueryException("Preferences not defined");
......@@ -533,7 +456,7 @@ public class UserRestImpl extends BaseRestImpl {
}
modifiedUser.setAnnotationSchema(schema);
getUserService().updateUser(modifiedUser);
return getUser(login, "preferences");
return new UserPreferencesDTO(getUser(login, "preferences").getAnnotationSchema());
} catch (IllegalArgumentException e) {
throw new QueryException("Invalid input", e);
} catch (Exception e) {
......@@ -574,7 +497,7 @@ public class UserRestImpl extends BaseRestImpl {
}
public Map<String, Object> updateUser(String login, Map<String, Object> userData,
public UserDTO updateUser(String login, Map<String, Object> userData,
Collection<? extends GrantedAuthority> authorities)
throws QueryException {
boolean isAdmin = authorities.contains(new SimpleGrantedAuthority(PrivilegeType.IS_ADMIN.toString()));
......@@ -622,7 +545,7 @@ public class UserRestImpl extends BaseRestImpl {
return getUser(login, "");
}
public Map<String, Object> addUser(String login, MultiValueMap<String, Object> userData) throws QueryException {
public UserDTO addUser(String login, MultiValueMap<String, Object> userData) throws QueryException {
User user = getUserService().getUserByLogin(login);
if (user != null) {
throw new ObjectExistsException("user exists");
......
......@@ -42,7 +42,6 @@ import org.springframework.mock.web.MockHttpSession;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.payload.RequestFieldsSnippet;
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
import org.springframework.restdocs.request.ParameterDescriptor;
import org.springframework.restdocs.request.RequestParametersSnippet;
import org.springframework.test.context.ActiveProfiles;
......@@ -813,13 +812,13 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.andDo(document("user/list_users",
userColumListRequestParam(),
responseFields(fieldWithPath("[]")
.description("list of available input formats")
.description("list of users")
.type(JsonFieldType.ARRAY)).andWithPrefix("[].", getUserResponseFields())))
.andExpect(status().is2xxSuccessful());
}
private List<FieldDescriptor> getUserResponseFields() {
return Arrays.asList(
List<FieldDescriptor> result = new ArrayList<>(Arrays.asList(
fieldWithPath("login")
.description("user login")
.optional()
......@@ -884,7 +883,9 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.description(
"type of privilege, available values: " + projectSnippets.getOptionsAsString(PrivilegeType.class))
.optional()
.type(JsonFieldType.STRING));
.type(JsonFieldType.STRING)));
result.addAll(getUserPreferencesResponseFields("", true));
return result;
}
private List<ParameterDescriptor> getUserRequestFieldsWithoutLogin() {
......@@ -945,7 +946,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.andDo(document("user/update_preferences_1",
pathParameters(parameterWithName("login").description("user login")),
updatePreferencesRequest(),
getUserPreferencesResponseFields()))
responseFields(getUserPreferencesResponseFields("", false))))
.andExpect(status().is2xxSuccessful());
callInSeparateThread(() -> {
......@@ -987,7 +988,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.andDo(document("user/update_preferences_2",
pathParameters(parameterWithName("login").description("user login")),
updatePreferencesRequest(),
getUserPreferencesResponseFields()))
responseFields(getUserPreferencesResponseFields("", false))))
.andExpect(status().is2xxSuccessful());
callInSeparateThread(() -> {
......@@ -1020,7 +1021,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.andDo(document("user/update_preferences_3",
pathParameters(parameterWithName("login").description("user login")),
updatePreferencesRequest(),
getUserPreferencesResponseFields()))
responseFields(getUserPreferencesResponseFields("", false))))
.andExpect(status().is2xxSuccessful());
callInSeparateThread(() -> {
......@@ -1049,7 +1050,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.andDo(document("user/update_preferences_4",
pathParameters(parameterWithName("login").description("user login")),
updatePreferencesRequest(),
getUserPreferencesResponseFields()))
responseFields(getUserPreferencesResponseFields("", false))))
.andExpect(status().is2xxSuccessful());
callInSeparateThread(() -> {
......@@ -1079,7 +1080,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
.andDo(document("user/update_preferences_5",
pathParameters(parameterWithName("login").description("user login")),
updatePreferencesRequest(),
getUserPreferencesResponseFields()))
responseFields(getUserPreferencesResponseFields("", false))))
.andExpect(status().is2xxSuccessful());
callInSeparateThread(() -> {
......@@ -1105,31 +1106,31 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
fieldWithPath("preferences")
.description("information about default preferences")
.type(JsonFieldType.OBJECT));
for (FieldDescriptor fd : getProjectUploadResponseFields()) {
for (FieldDescriptor fd : getProjectUploadResponseFields("")) {
if (fd.getDescription() != null) {
result = result.andWithPrefix("preferences.",
fieldWithPath(fd.getPath()).description(fd.getDescription()).type(fd.getType()).optional());
}
}
for (FieldDescriptor fd : getElementAnnotatorsResponseFields()) {
for (FieldDescriptor fd : getElementAnnotatorsResponseFields("")) {
if (fd.getDescription() != null) {
result = result.andWithPrefix("preferences.",
fieldWithPath(fd.getPath()).description(fd.getDescription()).type(fd.getType()).optional());
}
}
for (FieldDescriptor fd : getElementRequiredAnnotationResponseFields()) {
for (FieldDescriptor fd : getElementRequiredAnnotationResponseFields("")) {
if (fd.getDescription() != null) {
result = result.andWithPrefix("preferences.",
fieldWithPath(fd.getPath()).description(fd.getDescription()).type(fd.getType()).optional());
}
}
for (FieldDescriptor fd : getElementValidAnnotationResponseFields()) {
for (FieldDescriptor fd : getElementValidAnnotationResponseFields("")) {
if (fd.getDescription() != null) {
result = result.andWithPrefix("preferences.",
fieldWithPath(fd.getPath()).description(fd.getDescription()).type(fd.getType()).optional());
}
}
for (FieldDescriptor fd : getGuiPreferencesResponseFields()) {
for (FieldDescriptor fd : getGuiPreferencesResponseFields("")) {
if (fd.getDescription() != null) {
result = result.andWithPrefix("preferences.",
fieldWithPath(fd.getPath()).description(fd.getDescription()).type(fd.getType()).optional());
......@@ -1138,55 +1139,65 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
return result;
}
private ResponseFieldsSnippet getUserPreferencesResponseFields() {
return responseFields(fieldWithPath("preferences").description("set of user preferences").type(JsonFieldType.OBJECT))
.andWithPrefix("preferences.", getElementAnnotatorsResponseFields())
.andWithPrefix("preferences.", getElementRequiredAnnotationResponseFields())
.andWithPrefix("preferences.", getElementValidAnnotationResponseFields())
.andWithPrefix("preferences.", getGuiPreferencesResponseFields())
.andWithPrefix("preferences.", getProjectUploadResponseFields())
;
private List<FieldDescriptor> getUserPreferencesResponseFields(String prefix, boolean ignore) {
if (!prefix.isEmpty() && !prefix.endsWith(".")) {
prefix = prefix + ".preferences";
} else {
prefix = "preferences";
}
List<FieldDescriptor> fields = new ArrayList<>();
fields.add(fieldWithPath(prefix).description("set of user preferences").type(JsonFieldType.OBJECT));
fields.addAll(getElementAnnotatorsResponseFields(prefix + "."));
fields.addAll(getElementRequiredAnnotationResponseFields(prefix + "."));
fields.addAll(getElementValidAnnotationResponseFields(prefix + "."));
fields.addAll(getGuiPreferencesResponseFields(prefix + "."));
fields.addAll(getProjectUploadResponseFields(prefix + "."));
if (ignore) {
for (FieldDescriptor fieldDescriptor : fields) {
fieldDescriptor.ignored().optional();
}
}
return fields;
}
private List<FieldDescriptor> getElementAnnotatorsResponseFields() {
private List<FieldDescriptor> getElementAnnotatorsResponseFields(String prefix) {
return Arrays.asList(
fieldWithPath("element-annotators")
fieldWithPath(prefix + "element-annotators")
.description("definition of element annotators used by user")
.type(JsonFieldType.OBJECT),
fieldWithPath("element-annotators['*']")
fieldWithPath(prefix + "element-annotators['*']")
.description(
"list of annotators used for specific BioEntity type. Type of BioEntity is a class name that represents specific type in the system, for example: "
+ BioEntity.class.getName() + ", " + Complex.class.getName())
.type(JsonFieldType.ARRAY),
fieldWithPath("element-annotators['*'][].id")
fieldWithPath(prefix + "element-annotators['*'][].id")
.description("id")
.ignored()
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].annotatorClass")
fieldWithPath(prefix + "element-annotators['*'][].annotatorClass")
.description("type of annotator")
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].order")
fieldWithPath(prefix + "element-annotators['*'][].order")
.description("defines order in which annotators should be used")
.type(JsonFieldType.NUMBER),
fieldWithPath("element-annotators['*'][].parameters")
fieldWithPath(prefix + "element-annotators['*'][].parameters")
.description("list of parameters for the annotator")
.type(JsonFieldType.ARRAY),
fieldWithPath("element-annotators['*'][].parameters[].type")
fieldWithPath(prefix + "element-annotators['*'][].parameters[].type")
.description(
"is this parameter INPUT/OUTPUT/CONFIG type. "
+ "INPUT types define what should be used to identify the element. "
+ "OUTPUT parameter defines what should be written to the element.")
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].parameters[].annotation_type")
fieldWithPath(prefix + "element-annotators['*'][].parameters[].annotation_type")
.description("specifies annotation type to be used (if empty then field will be used)")
.optional()
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].parameters[].field")
fieldWithPath(prefix + "element-annotators['*'][].parameters[].field")
.description("specifies field to be used")
.optional()
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].parameters[].order")
fieldWithPath(prefix + "element-annotators['*'][].parameters[].order")
.description(
"input parameters are checked in order. First match identifies the element. This field defines what is the order of INPUT parameters.")
.optional()
......@@ -1195,67 +1206,67 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
);
}
private List<FieldDescriptor> getElementRequiredAnnotationResponseFields() {
private List<FieldDescriptor> getElementRequiredAnnotationResponseFields(String prefix) {
return Arrays.asList(
fieldWithPath("element-required-annotations")
fieldWithPath(prefix + "element-required-annotations")
.description("definition of required annotation by BioEntity type.")
.type(JsonFieldType.OBJECT),
fieldWithPath("element-required-annotations['*']")
fieldWithPath(prefix + "element-required-annotations['*']")
.description(
"definition of required annotations for specific BioEntity type")
.type(JsonFieldType.OBJECT),
fieldWithPath("element-required-annotations['*'].require-at-least-one")
fieldWithPath(prefix + "element-required-annotations['*'].require-at-least-one")
.description("is at least one annotation required")
.type(JsonFieldType.BOOLEAN),
fieldWithPath("element-required-annotations['*'].annotation-list")
fieldWithPath(prefix + "element-required-annotations['*'].annotation-list")
.description("list of annotation types from which at least one is required")
.type(JsonFieldType.ARRAY));
}
private List<FieldDescriptor> getElementValidAnnotationResponseFields() {
private List<FieldDescriptor> getElementValidAnnotationResponseFields(String prefix) {
return Arrays.asList(
fieldWithPath("element-valid-annotations")
fieldWithPath(prefix + "element-valid-annotations")
.description("definition of valid annotation by BioEntity type.")
.type(JsonFieldType.OBJECT),
fieldWithPath("element-valid-annotations['*'][]")
fieldWithPath(prefix + "element-valid-annotations['*'][]")
.description(
"list of annotation types that is valid for specific BioEntity type")
.type(JsonFieldType.ARRAY