Commit 7d1ac0cf authored by Piotr Gawron's avatar Piotr Gawron
Browse files

respect mapId

parent 8df23bba
......@@ -3,6 +3,8 @@ minerva (16.0.6) stable; urgency=medium
reactions and elements are fetched in parallel (#1605)
* Bug fix (performance): showing data overlay is faster on projects with
multiple maps
* Bug fix: REST API did not respect mapId when listing overlay elements in a
project
* Bug fix: log4j2 updated to address security issue CVE-2021-44832
-- Piotr Gawron <piotr.gawron@uni.lu> Thu, 16 Dec 2021 11:00:00 +0200
......
......@@ -9,9 +9,19 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.*;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import lcsb.mapviewer.api.BaseController;
import lcsb.mapviewer.api.SerializationUtils;
......@@ -30,8 +40,8 @@ public class OverlayController extends BaseController {
private OverlayRestImpl overlayRestImp;
private IUserService userService;
public OverlayController(OverlayRestImpl overlayRestImp,
IUserService userService) {
public OverlayController(final OverlayRestImpl overlayRestImp,
final IUserService userService) {
this.overlayRestImp = overlayRestImp;
this.userService = userService;
}
......@@ -43,9 +53,9 @@ public class OverlayController extends BaseController {
" or hasAuthority('READ_PROJECT:' + #projectId) and (filterObject['creator'] == authentication.name or filterObject['publicOverlay'])")
@GetMapping(value = "/")
public List<Map<String, Object>> getOverlayList(
@PathVariable(value = "projectId") String projectId,
@RequestParam(value = "creator", defaultValue = "") String creator,
@RequestParam(value = "publicOverlay", defaultValue = "false") boolean publicOverlay)
@PathVariable(value = "projectId") final String projectId,
@RequestParam(value = "creator", defaultValue = "") final String creator,
@RequestParam(value = "publicOverlay", defaultValue = "false") final boolean publicOverlay)
throws lcsb.mapviewer.services.ObjectNotFoundException {
return overlayRestImp.getOverlayList(projectId).stream()
.filter(overlay -> !publicOverlay || (Boolean) overlay.get("publicOverlay"))
......@@ -59,8 +69,8 @@ public class OverlayController extends BaseController {
" or hasAuthority('READ_PROJECT:' + #projectId) and (returnObject['creator'] == authentication.name or returnObject['publicOverlay'])")
@GetMapping(value = "/{overlayId}/")
public Map<String, Object> getOverlayById(
@PathVariable(value = "projectId") String projectId,
@PathVariable(value = "overlayId") Integer overlayId) throws QueryException {
@PathVariable(value = "projectId") final String projectId,
@PathVariable(value = "overlayId") final Integer overlayId) throws QueryException {
return overlayRestImp.getOverlayById(projectId, overlayId);
}
......@@ -71,11 +81,12 @@ public class OverlayController extends BaseController {
" or @dataOverlayService.getDataOverlayById(this.getId(#overlayId))?.public == true)")
@GetMapping(value = "/{overlayId}/models/{modelId}/bioEntities/")
public List<Map<String, Object>> getOverlayElements(
@PathVariable(value = "projectId") String projectId,
@PathVariable(value = "overlayId") String overlayId,
@RequestParam(value = "columns", defaultValue = "") String columns) throws QueryException {
@PathVariable(value = "projectId") final String projectId,
@PathVariable(value = "modelId") final String modelId,
@PathVariable(value = "overlayId") final String overlayId,
@RequestParam(value = "columns", defaultValue = "") final String columns) throws QueryException {
return overlayRestImp.getOverlayElements(projectId, SerializationUtils.parseInteger(overlayId, "overlayId"),
return overlayRestImp.getOverlayElements(projectId, modelId, SerializationUtils.parseInteger(overlayId, "overlayId"),
columns);
}
......@@ -85,11 +96,11 @@ public class OverlayController extends BaseController {
" (@dataOverlayService.getOverlayCreator(#overlayId)?.login == authentication.name or @dataOverlayService.getDataOverlayById(#overlayId)?.public == true)")
@GetMapping(value = "/{overlayId}/models/{modelId}/bioEntities/reactions/{reactionId}/")
public List<Map<String, Object>> getFullReaction(
@PathVariable(value = "projectId") String projectId,
@PathVariable(value = "modelId") String modelId,
@PathVariable(value = "overlayId") Integer overlayId,
@PathVariable(value = "reactionId") String reactionId,
@RequestParam(value = "columns", defaultValue = "") String columns)
@PathVariable(value = "projectId") final String projectId,
@PathVariable(value = "modelId") final String modelId,
@PathVariable(value = "overlayId") final Integer overlayId,
@PathVariable(value = "reactionId") final String reactionId,
@RequestParam(value = "columns", defaultValue = "") final String columns)
throws QueryException, NumberFormatException, ObjectNotFoundException {
return overlayRestImp.getOverlayElement(projectId,
......@@ -105,11 +116,11 @@ public class OverlayController extends BaseController {
" (@dataOverlayService.getOverlayCreator(#overlayId)?.login == authentication.name or @dataOverlayService.getDataOverlayById(#overlayId)?.public == true)")
@GetMapping(value = "/{overlayId}/models/{modelId}/bioEntities/elements/{elementId}/")
public List<Map<String, Object>> getFullSpecies(
@PathVariable(value = "projectId") String projectId,
@PathVariable(value = "modelId") String modelId,
@PathVariable(value = "overlayId") Integer overlayId,
@PathVariable(value = "elementId") String reactionId,
@RequestParam(value = "columns", defaultValue = "") String columns)
@PathVariable(value = "projectId") final String projectId,
@PathVariable(value = "modelId") final String modelId,
@PathVariable(value = "overlayId") final Integer overlayId,
@PathVariable(value = "elementId") final String reactionId,
@RequestParam(value = "columns", defaultValue = "") final String columns)
throws QueryException, NumberFormatException, ObjectNotFoundException {
return overlayRestImp.getOverlayElement(projectId,
SerializationUtils.parseInteger(modelId, "modelId"),
......@@ -124,15 +135,15 @@ public class OverlayController extends BaseController {
" or (hasAuthority('READ_PROJECT:' + #projectId) and hasAuthority('CAN_CREATE_OVERLAYS'))")
@PostMapping(value = "/")
public Map<String, Object> addOverlay(
Authentication authentication,
@PathVariable(value = "projectId") String projectId,
@RequestParam(value = "name") String name,
@RequestParam(value = "description") String description,
@RequestParam(value = "content", defaultValue = "") String content,
@RequestParam(value = "fileId", defaultValue = "") String fileId,
@RequestParam(value = "filename") String filename,
@RequestParam(value = "googleLicenseConsent") String googleLicenseConsent,
@RequestParam(value = "type", defaultValue = "") String type)
final Authentication authentication,
@PathVariable(value = "projectId") final String projectId,
@RequestParam(value = "name") final String name,
@RequestParam(value = "description") final String description,
@RequestParam(value = "content", defaultValue = "") final String content,
@RequestParam(value = "fileId", defaultValue = "") final String fileId,
@RequestParam(value = "filename") final String filename,
@RequestParam(value = "googleLicenseConsent") final String googleLicenseConsent,
@RequestParam(value = "type", defaultValue = "") final String type)
throws QueryException, IOException, SecurityException {
User user = userService.getUserByLogin(authentication.getName());
return overlayRestImp.addOverlay(
......@@ -144,8 +155,8 @@ public class OverlayController extends BaseController {
"or hasAuthority('READ_PROJECT:' + #projectId) and @dataOverlayService.getOverlayCreator(#overlayId)?.login == authentication.name")
@DeleteMapping(value = "/{overlayId}")
public Map<String, Object> removeOverlay(
@PathVariable(value = "projectId") String projectId,
@PathVariable(value = "overlayId") Integer overlayId) throws QueryException, IOException {
@PathVariable(value = "projectId") final String projectId,
@PathVariable(value = "overlayId") final Integer overlayId) throws QueryException, IOException {
return overlayRestImp.removeOverlay(projectId, overlayId);
}
......@@ -154,9 +165,9 @@ public class OverlayController extends BaseController {
" or hasAuthority('READ_PROJECT:' + #projectId) and @dataOverlayService.getOverlayCreator(#overlayId)?.login == authentication.name")
@PatchMapping(value = "/{overlayId}")
public Map<String, Object> updateOverlay(
@RequestBody String body,
@PathVariable(value = "overlayId") Integer overlayId,
@PathVariable(value = "projectId") String projectId)
@RequestBody final String body,
@PathVariable(value = "overlayId") final Integer overlayId,
@PathVariable(value = "projectId") final String projectId)
throws QueryException, IOException {
Map<String, Object> node = parseBody(body);
Map<String, Object> data = getData(node, "overlay");
......@@ -169,8 +180,8 @@ public class OverlayController extends BaseController {
" (@dataOverlayService.getOverlayCreator(#overlayId)?.login == authentication.name or @dataOverlayService.getDataOverlayById(#overlayId)?.public == true)")
@GetMapping(value = "/{overlayId}:downloadSource")
public ResponseEntity<byte[]> getOverlaySource(
@PathVariable(value = "projectId") String projectId,
@PathVariable(value = "overlayId") Integer overlayId)
@PathVariable(value = "projectId") final String projectId,
@PathVariable(value = "overlayId") final Integer overlayId)
throws QueryException {
FileEntry file = overlayRestImp.getOverlaySource(projectId, overlayId);
......
......@@ -109,11 +109,11 @@ public class OverlayRestImpl extends BaseRestImpl {
return result;
}
public List<Map<String, Object>> getOverlayElements(final String projectId, final int overlayId, final String columns)
public List<Map<String, Object>> getOverlayElements(final String projectId, final String modelId, final int overlayId, final String columns)
throws QueryException {
dataOverlayService.getDataOverlayById(projectId, overlayId);
List<ModelData> models = modelService.getModelsByMapId(projectId, "*");
List<ModelData> models = modelService.getModelsByMapId(projectId, modelId);
String[] columnSet;
if (columns != null && !columns.trim().isEmpty()) {
......
package lcsb.mapviewer.web;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.restdocs.payload.*;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
import org.springframework.restdocs.request.RequestParametersSnippet;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
......@@ -304,7 +318,7 @@ public class OverlayControllerIntegrationTest extends ControllerIntegrationTest
RequestBuilder request = get(
"/projects/{projectId}/overlays/{overlayId}/models/{mapId}/bioEntities/",
TEST_PROJECT, overlay.getId(), map.getId())
TEST_PROJECT, overlay.getId(), "*")
.session(session);
String response = mockMvc.perform(request)
......@@ -322,6 +336,49 @@ public class OverlayControllerIntegrationTest extends ControllerIntegrationTest
.getAsJsonArray().size());
}
@Test
public void testGetBioEntitiesForSpecificSubmap() throws Exception {
List<ModelData> models = new ArrayList<>(project.getModels());
ModelData map1 = models.get(0);
ModelData map2 = models.get(1);
DataOverlay overlay = createOverlayInSeparateThread(user);
MockHttpSession session = createSession(TEST_USER_LOGIN, TEST_USER_PASSWORD);
RequestBuilder requestAll = get(
"/projects/{projectId}/overlays/{overlayId}/models/{mapId}/bioEntities/",
TEST_PROJECT, overlay.getId(), "*")
.session(session);
RequestBuilder request1 = get(
"/projects/{projectId}/overlays/{overlayId}/models/{mapId}/bioEntities/",
TEST_PROJECT, overlay.getId(), map1.getId())
.session(session);
RequestBuilder request2 = get(
"/projects/{projectId}/overlays/{overlayId}/models/{mapId}/bioEntities/",
TEST_PROJECT, overlay.getId(), map2.getId())
.session(session);
String responseAll = mockMvc.perform(requestAll)
.andExpect(status().is2xxSuccessful())
.andReturn().getResponse().getContentAsString();
String response1 = mockMvc.perform(request1)
.andExpect(status().is2xxSuccessful())
.andReturn().getResponse().getContentAsString();
String response2 = mockMvc.perform(request2)
.andExpect(status().is2xxSuccessful())
.andReturn().getResponse().getContentAsString();
int countAll = new JsonParser().parse(responseAll).getAsJsonArray().size();
int count1 = new JsonParser().parse(response1).getAsJsonArray().size();
int count2 = new JsonParser().parse(response2).getAsJsonArray().size();
assertTrue(String.format("Too many results %d < %d + %d", countAll, count1, count2), countAll >= count1 + count2);
}
@Test
public void testGetBioEntitiesForOverlayAsUser() throws Exception {
DataOverlay overlay = createOverlayInSeparateThread(user);
......@@ -329,7 +386,7 @@ public class OverlayControllerIntegrationTest extends ControllerIntegrationTest
MockHttpSession session = createSession(TEST_USER_LOGIN, TEST_USER_PASSWORD);
RequestBuilder request = get(
"/projects/" + TEST_PROJECT + "/overlays/" + overlay.getId() + "/models/" + map.getId() + "/bioEntities/")
"/projects/" + TEST_PROJECT + "/overlays/" + overlay.getId() + "/models/*/bioEntities/")
.session(session);
String response = mockMvc.perform(request)
......@@ -373,7 +430,7 @@ public class OverlayControllerIntegrationTest extends ControllerIntegrationTest
MockHttpSession session = createSession(TEST_CURATOR_LOGIN, TEST_CURATOR_PASSWORD);
RequestBuilder request = get(
"/projects/" + TEST_PROJECT + "/overlays/" + overlay.getId() + "/models/" + map.getId() + "/bioEntities/")
"/projects/" + TEST_PROJECT + "/overlays/" + overlay.getId() + "/models/*/bioEntities/")
.session(session);
String response = mockMvc.perform(request)
......@@ -959,7 +1016,7 @@ public class OverlayControllerIntegrationTest extends ControllerIntegrationTest
.andExpect(status().is2xxSuccessful());
}
private DataOverlay createOverlayInSeparateThread(User user) throws Exception {
private DataOverlay createOverlayInSeparateThread(final User user) throws Exception {
return createOverlayInSeparateThread(TEST_PROJECT, user);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment