Commit b6df252c authored by Piotr Gawron's avatar Piotr Gawron
Browse files

UserAnnotationSerializer implemented

parent 32c6bf5e
......@@ -10,9 +10,12 @@ import org.apache.logging.log4j.Logger;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.model.map.MiriamType;
import lcsb.mapviewer.model.user.annotator.AnnotatorData;
import lcsb.mapviewer.modelutils.serializer.model.user.UserAnnotationSchemaSerializer;
/**
* Annotation schema used by the user. It contains information about annotators
......@@ -23,6 +26,7 @@ import lcsb.mapviewer.model.user.annotator.AnnotatorData;
*
*/
@Entity
@JsonSerialize(using = UserAnnotationSchemaSerializer.class)
public class UserAnnotationSchema implements Serializable {
/**
......
......@@ -8,6 +8,9 @@ import javax.persistence.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lcsb.mapviewer.model.map.MiriamType;
/**
......@@ -36,6 +39,7 @@ public class UserClassRequiredAnnotations implements Serializable {
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonIgnore
private Integer id;
/**
......@@ -43,17 +47,20 @@ public class UserClassRequiredAnnotations implements Serializable {
* annotations} is used.
*/
@ManyToOne
@JsonIgnore
private UserAnnotationSchema annotationSchema;
/**
* Name of the class for which this set is defined.
*/
@Column(length = 255)
@JsonIgnore
private String className;
/**
* Are the annotations required?
*/
@JsonProperty("require-at-least-one")
private Boolean requireAtLeastOneAnnotation;
/**
......@@ -65,6 +72,7 @@ public class UserClassRequiredAnnotations implements Serializable {
@Column(name = "miriam_type_name", nullable = false)
@OrderColumn(name = "idx")
@Enumerated(EnumType.STRING)
@JsonProperty("annotation-list")
private List<MiriamType> requiredMiriamTypes = new ArrayList<>();
/**
......
......@@ -4,13 +4,25 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lcsb.mapviewer.model.user.AnnotatorParamDefinition;
import lcsb.mapviewer.model.user.UserClassAnnotators;
import lcsb.mapviewer.modelutils.serializer.ClassNameSerializer;
@Entity
public class AnnotatorData implements Serializable {
......@@ -30,6 +42,7 @@ public class AnnotatorData implements Serializable {
/**
* Order of the annotator in the annotator list.
*/
@JsonProperty("order")
private int orderIndex;
/**
......@@ -37,9 +50,12 @@ public class AnnotatorData implements Serializable {
* being set to the {@link #paramValue value}.
*/
@Column(nullable = false)
@JsonProperty("annotatorClass")
@JsonSerialize(using = ClassNameSerializer.class)
private Class<?> annotatorClassName;
@ManyToOne
@JsonIgnore
private UserClassAnnotators userClassAnnotators;
/**
......@@ -48,6 +64,7 @@ public class AnnotatorData implements Serializable {
@Cascade({ CascadeType.ALL })
@OneToMany(mappedBy = "annotatorData")
@OrderBy("id")
@JsonProperty("parameters")
private List<AnnotatorParameter> annotatorParams = new ArrayList<>();
/**
......@@ -156,6 +173,7 @@ public class AnnotatorData implements Serializable {
return null;
}
@JsonIgnore
public List<AnnotatorInputParameter> getInputParameters() {
List<AnnotatorInputParameter> result = new ArrayList<>();
for (AnnotatorParameter annotatorParameter : annotatorParams) {
......@@ -175,6 +193,7 @@ public class AnnotatorData implements Serializable {
return false;
}
@JsonIgnore
public List<AnnotatorOutputParameter> getOutputParameters() {
List<AnnotatorOutputParameter> result = new ArrayList<>();
for (AnnotatorParameter annotatorParameter : annotatorParams) {
......
package lcsb.mapviewer.modelutils.serializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class ClassNameSerializer extends JsonSerializer<Class<?>> {
@Override
public void serialize(final Class<?> entry, final JsonGenerator gen,
final SerializerProvider serializers)
throws IOException {
gen.writeString(entry.getName());
}
}
\ No newline at end of file
package lcsb.mapviewer.modelutils.serializer.model.user;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lcsb.mapviewer.model.map.MiriamType;
import lcsb.mapviewer.model.user.UserAnnotationSchema;
import lcsb.mapviewer.model.user.UserClassAnnotators;
import lcsb.mapviewer.model.user.UserClassRequiredAnnotations;
import lcsb.mapviewer.model.user.UserClassValidAnnotations;
import lcsb.mapviewer.model.user.UserGuiPreference;
public class UserAnnotationSchemaSerializer extends JsonSerializer<UserAnnotationSchema> {
@Override
public void serialize(final UserAnnotationSchema schema, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeObjectFieldStart("project-upload");
gen.writeBooleanField("validate-miriam", schema.getValidateMiriamTypes());
gen.writeBooleanField("annotate-model", schema.getAnnotateModel());
gen.writeBooleanField("cache-data", schema.getCacheData());
gen.writeBooleanField("auto-resize", schema.getAutoResizeMap());
gen.writeBooleanField("semantic-zooming-contains-multiple-overlays", schema.getSemanticZoomContainsMultipleOverlays());
gen.writeBooleanField("sbgn", schema.getSbgnFormat());
gen.writeEndObject();
gen.writeObjectFieldStart("element-annotators");
for (UserClassAnnotators userClassAnnotators : schema.getClassAnnotators()) {
gen.writeObjectField(userClassAnnotators.getClassName(), userClassAnnotators.getAnnotators());
}
gen.writeEndObject();
gen.writeObjectFieldStart("element-required-annotations");
for (UserClassRequiredAnnotations requiredAnnotations : schema.getClassRequiredAnnotators()) {
gen.writeObjectField(requiredAnnotations.getClassName(), requiredAnnotations);
}
gen.writeEndObject();
gen.writeObjectFieldStart("element-valid-annotations");
for (UserClassValidAnnotations userClassAnnotators : schema.getClassValidAnnotators()) {
gen.writeObjectField(userClassAnnotators.getClassName(), userClassAnnotators.getValidMiriamTypes());
}
gen.writeEndObject();
gen.writeObjectFieldStart("gui-preferences");
for (UserGuiPreference userGuiPreference : schema.getGuiPreferences()) {
gen.writeObjectField(userGuiPreference.getKey(), userGuiPreference.getValue());
}
gen.writeEndObject();
gen.writeEndObject();
}
}
\ No newline at end of file
package lcsb.mapviewer.api.users;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
......@@ -26,13 +24,9 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import lcsb.mapviewer.api.BaseRestImpl;
import lcsb.mapviewer.api.OperationNotAllowedException;
import lcsb.mapviewer.api.UpdateConflictException;
import lcsb.mapviewer.common.Configuration;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.model.map.MiriamType;
import lcsb.mapviewer.model.map.layout.ProjectBackground;
import lcsb.mapviewer.model.security.Privilege;
import lcsb.mapviewer.model.security.PrivilegeType;
import lcsb.mapviewer.model.user.AnnotatorParamDefinition;
import lcsb.mapviewer.model.user.User;
......@@ -40,7 +34,6 @@ import lcsb.mapviewer.model.user.UserAnnotationSchema;
import lcsb.mapviewer.model.user.UserClassAnnotators;
import lcsb.mapviewer.model.user.UserClassRequiredAnnotations;
import lcsb.mapviewer.model.user.UserClassValidAnnotations;
import lcsb.mapviewer.model.user.UserGuiPreference;
import lcsb.mapviewer.model.user.annotator.AnnotatorConfigParameter;
import lcsb.mapviewer.model.user.annotator.AnnotatorData;
import lcsb.mapviewer.model.user.annotator.AnnotatorInputParameter;
......@@ -62,14 +55,11 @@ public class UserRestImpl extends BaseRestImpl {
@SuppressWarnings("unused")
private Logger logger = LogManager.getLogger();
private PasswordEncoder passwordEncoder;
private IProjectBackgroundService projectBackgroundService;
@Autowired
public UserRestImpl(PasswordEncoder passwordEncoder,
public UserRestImpl(PasswordEncoder passwordEncoder,
IProjectBackgroundService projectBackgroundService) {
this.passwordEncoder = passwordEncoder;
this.projectBackgroundService = projectBackgroundService;
}
public Map<String, Object> getUser(String login, String columns)
......@@ -164,7 +154,7 @@ public class UserRestImpl extends BaseRestImpl {
value = user.getPrivileges();
break;
case "preferences":
value = preparePreferences(user);
value = user.getAnnotationSchema();
break;
default:
value = "Unknown column";
......@@ -175,33 +165,6 @@ public class UserRestImpl extends BaseRestImpl {
return result;
}
Map<String, Object> preparePreferences(User user) {
Map<String, Object> result = new TreeMap<>();
UserAnnotationSchema schema = getProjectService().prepareUserAnnotationSchema(user);
result.put("project-upload", prepareProjectUploadPreferences(schema));
result.put("element-annotators", prepareElementAnnotators(schema.getClassAnnotators()));
result.put("element-required-annotations", prepareRequiredAnnotations(schema.getClassRequiredAnnotators()));
result.put("element-valid-annotations", prepareValidAnnotations(schema.getClassValidAnnotators()));
result.put("gui-preferences", prepareGuiPreferences(schema.getGuiPreferences()));
return result;
}
Map<String, Object> prepareGuiPreferences(Set<UserGuiPreference> guiPreferences) {
Map<String, Object> result = new TreeMap<>();
for (UserGuiPreference userGuiPreference : guiPreferences) {
result.put(userGuiPreference.getKey(), userGuiPreference.getValue());
}
return result;
}
private Map<String, Object> prepareValidAnnotations(List<UserClassValidAnnotations> classValidAnnotators) {
Map<String, Object> result = new TreeMap<>();
for (UserClassValidAnnotations userClassAnnotators : classValidAnnotators) {
result.put(userClassAnnotators.getClassName(), new ArrayList<>(userClassAnnotators.getValidMiriamTypes()));
}
return result;
}
private void updateValidAnnotations(UserAnnotationSchema schema, Map<String, Object> data) {
for (String key : data.keySet()) {
UserClassValidAnnotations annotator = null;
......@@ -237,22 +200,6 @@ public class UserRestImpl extends BaseRestImpl {
}
}
private Map<String, Object> prepareRequiredAnnotations(List<UserClassRequiredAnnotations> classRequiredAnnotators) {
Map<String, Object> result = new TreeMap<>();
for (UserClassRequiredAnnotations requiredAnnotations : classRequiredAnnotators) {
Map<String, Object> row = new TreeMap<>();
row.put("require-at-least-one", requiredAnnotations.getRequireAtLeastOneAnnotation());
List<String> miriamTypes = new ArrayList<>();
for (MiriamType mt : requiredAnnotations.getRequiredMiriamTypes()) {
miriamTypes.add(mt.name());
}
row.put("annotation-list", miriamTypes);
result.put(requiredAnnotations.getClassName(), row);
}
return result;
}
private void updateRequiredAnnotations(UserAnnotationSchema schema, Map<String, Object> data) throws QueryException {
for (String key : data.keySet()) {
UserClassRequiredAnnotations annotator = null;
......@@ -286,31 +233,6 @@ public class UserRestImpl extends BaseRestImpl {
}
private Map<String, Object> prepareElementAnnotators(List<UserClassAnnotators> classAnnotators) {
Map<String, Object> result = new TreeMap<>();
for (UserClassAnnotators userClassAnnotators : classAnnotators) {
result.put(userClassAnnotators.getClassName(), prepareAnnotators(userClassAnnotators.getAnnotators()));
}
return result;
}
private List<Map<String, Object>> prepareAnnotators(List<AnnotatorData> annotators) {
List<Map<String, Object>> result = new ArrayList<>();
for (AnnotatorData annotatorData : annotators) {
result.add(prepareAnnotator(annotatorData));
}
return result;
}
private Map<String, Object> prepareAnnotator(AnnotatorData annotatorData) {
Map<String, Object> result = new HashMap<>();
result.put("annotatorClass", annotatorData.getAnnotatorClassName().getName());
result.put("id", annotatorData.getId());
result.put("order", annotatorData.getOrderIndex());
result.put("parameters", annotatorData.getAnnotatorParams());
return result;
}
private AnnotatorData parseAnnotator(Map<String, Object> map) throws QueryException {
try {
if (map.get("annotatorClass") == null) {
......
......@@ -82,24 +82,4 @@ public class UserRestImplTest extends RestTestFunctions {
Object response = userRestImpl.getUsers("");
assertNotNull(response);
}
@Test
public void testPrepareGuiPreferencesSimple() throws Exception {
Map<String, Object> response = userRestImpl.prepareGuiPreferences(new HashSet<>());
assertNotNull(response);
}
@Test
public void testPrepareGuiPreferencesWithOption() throws Exception {
Set<UserGuiPreference> guiPreferences = new HashSet<>();
UserGuiPreference option = new UserGuiPreference();
option.setKey("TestKey");
option.setValue("TestValue");
guiPreferences.add(option);
Map<String, Object> response = userRestImpl.prepareGuiPreferences(guiPreferences);
assertNotNull(response);
assertEquals("TestValue", response.get(option.getKey()));
}
}
......@@ -189,7 +189,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
return requestFields(
fieldWithPath("privileges.*")
.description("should the privilege defined as PRIVILEGE_TYPE:ID_OBJECT be granted or not")
.type("boolean"));
.type(JsonFieldType.BOOLEAN));
}
@Test
......@@ -792,23 +792,23 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
fieldWithPath("login")
.description("user login")
.optional()
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("connectedToLdap")
.description("is user account connected to ldap")
.optional()
.type("boolean"),
.type(JsonFieldType.BOOLEAN),
fieldWithPath("ldapAccountAvailable")
.description("does is account exist in ldap")
.optional()
.type("boolean"),
.type(JsonFieldType.BOOLEAN),
fieldWithPath("email")
.description("email address")
.optional()
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("id")
.description("user unique id")
.optional()
.type("number"),
.type(JsonFieldType.NUMBER),
fieldWithPath("maxColor")
.description("color used for drawing data overlays with max value")
.optional()
......@@ -828,19 +828,19 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
fieldWithPath("name")
.description("first name")
.optional()
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("surname")
.description("last name")
.optional()
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("removed")
.description("is the account removed")
.optional()
.type("boolean"),
.type(JsonFieldType.BOOLEAN),
fieldWithPath("termsOfUseConsent")
.description("did user agree to terms of use")
.optional()
.type("boolean"),
.type(JsonFieldType.BOOLEAN),
fieldWithPath("privileges")
.description("list of user privileges")
.optional()
......@@ -1083,7 +1083,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
RequestFieldsSnippet result = requestFields(
fieldWithPath("preferences")
.description("information about default preferences")
.type("object"));
.type(JsonFieldType.OBJECT));
for (FieldDescriptor fd : getProjectUploadResponseFields()) {
if (fd.getDescription() != null) {
result = result.andWithPrefix("preferences.",
......@@ -1118,7 +1118,7 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
}
private ResponseFieldsSnippet getUserPreferencesResponseFields() {
return responseFields(fieldWithPath("preferences").description("set of user preferences").type("object"))
return responseFields(fieldWithPath("preferences").description("set of user preferences").type(JsonFieldType.OBJECT))
.andWithPrefix("preferences.", getElementAnnotatorsResponseFields())
.andWithPrefix("preferences.", getElementRequiredAnnotationResponseFields())
.andWithPrefix("preferences.", getElementValidAnnotationResponseFields())
......@@ -1132,44 +1132,44 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
return Arrays.asList(
fieldWithPath("element-annotators")
.description("definition of element annotators used by user")
.type("object"),
.type(JsonFieldType.OBJECT),
fieldWithPath("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("array"),
.type(JsonFieldType.ARRAY),
fieldWithPath("element-annotators['*'][].id")
.description("id")
.ignored()
.type("number"),
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].annotatorClass")
.description("type of annotator")
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].order")
.description("defines order in which annotators should be used")
.type("number"),
.type(JsonFieldType.NUMBER),
fieldWithPath("element-annotators['*'][].parameters")
.description("list of parameters for the annotator")
.type("object"),
.type(JsonFieldType.ARRAY),
fieldWithPath("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("string"),
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].parameters[].annotation_type")
.description("specifies annotation type to be used (if empty then field will be used)")
.optional()
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("element-annotators['*'][].parameters[].field")
.description("specifies field to be used")
.optional()
.type("string"),
.type(JsonFieldType.STRING),
fieldWithPath("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()
.type("number")
.type(JsonFieldType.NUMBER)
);
}
......@@ -1178,65 +1178,65 @@ public class UserControllerIntegrationTest extends ControllerIntegrationTest {
return Arrays.asList(
fieldWithPath("element-required-annotations")
.description("definition of required annotation by BioEntity type.")
.type("object"),
.type(JsonFieldType.OBJECT),
fieldWithPath("element-required-annotations['*']")
.description(
"definition of required annotations for specific BioEntity type")
.type("object"),
.type(JsonFieldType.OBJECT),
fieldWithPath("element-required-annotations['*'].require-at-least-one")
.description("is at least one annotation required")
.type("boolean"),
.type(JsonFieldType.BOOLEAN),
fieldWithPath("element-required-annotations['*'].annotation-list")
.description("list of annotation types from which at least one is required")
.type("array"));
.type(JsonFieldType.ARRAY));
}
private List<FieldDescriptor> getElementValidAnnotationResponseFields() {
return Arrays.asList(
fieldWithPath("element-valid-annotations")
.description("definition of valid annotation by BioEntity type.")
.type("object"),
.type(JsonFieldType.OBJECT),
fieldWithPath("element-valid-annotations['*'][]")
.description(
"list of annotation types that is valid for specific BioEntity type")
.type("array"));
.type(JsonFieldType.ARRAY));
}
private List<FieldDescriptor> getGuiPreferencesResponseFields() {
return Arrays.asList(
fieldWithPath("gui-preferences")
.description("definition of user gui preferences.")
.type("object"),
.type(JsonFieldType.OBJECT),
fieldWithPath("gui-preferences.*")
.optional()
.description(
"key-value definition of gui preference used for remembering default sort order, default pagination, etc")
.type("string"));
.type(JsonFieldType.STRING));
}
private List<FieldDescriptor> getProjectUploadResponseFields() {
return Arrays.asList(
fieldWithPath("project-upload")
.description("definition of preferences for project-upload.")
.type("object"),
.type(JsonFieldType.OBJECT),
fieldWithPath("project-upload.annotate-model")
.description("annotate model after it is uploaded.")
.type("boolean"),
.type(JsonFieldType.BOOLEAN),
fie