Added delete query webservice and tests.
Change-Id: I5693aa9712a366d3b03ffbf67fa49e6c9da28515
diff --git a/full/Changes b/full/Changes
index 45ff8ec..8d3d4a2 100644
--- a/full/Changes
+++ b/full/Changes
@@ -3,7 +3,8 @@
- Updated libraries (margaretha)
- Renamed virtual corpus to query (margaretha)
2021-02-26
- - Added query access roles and fixed vc access roles.
+ - Added query access roles and fixed vc access roles (margaretha)
+ - Added delete query webservice and tests (margaretha)
# version 0.63
26/10/2020
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
index faa150c..41faa2a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
@@ -98,7 +98,8 @@
if (DEBUG) {
jlog.debug("Delete existing vc: " + filename);
}
- vcService.deleteVC("system", vc.getId());
+ vcService.deleteQueryByName("system", vc.getName(),
+ vc.getCreatedBy(), QueryType.VIRTUAL_CORPUS);
}
}
catch (KustvaktException e) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/QueryDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/QueryDao.java
index 27d3b9f..9fa399c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/QueryDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/QueryDao.java
@@ -40,7 +40,7 @@
/**
* QueryDao manages database queries and transactions
- * regarding KorAP queries, e.g. retrieving and storing queries.
+ * regarding virtual corpus and KorAP queries.
*
* @author margaretha
*
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java b/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java
index 2289a47..3d60f21 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/QueryService.java
@@ -42,14 +42,22 @@
import de.ids_mannheim.korap.utils.KoralCollectionQueryBuilder;
import de.ids_mannheim.korap.utils.ParameterChecker;
import de.ids_mannheim.korap.web.SearchKrill;
+import de.ids_mannheim.korap.web.controller.QueryReferenceController;
import de.ids_mannheim.korap.web.controller.VirtualCorpusController;
import de.ids_mannheim.korap.web.input.QueryJson;
/**
- * VirtualCorpusService handles the logic behind
- * {@link VirtualCorpusController}.
- * It communicates with {@link QueryDao} and returns
- * {@link QueryDto} to {@link VirtualCorpusController}.
+ * QueryService handles the logic behind
+ * {@link VirtualCorpusController} and
+ * {@link QueryReferenceController}. Virtual corpora and
+ * stored-queries are both treated as queries of different types.
+ * Thus, they are handled logically similarly.
+ *
+ * QueryService communicates with {@link QueryDao}, handles
+ * {@link QueryDO} and
+ * returns
+ * {@link QueryDto} to {@link VirtualCorpusController} and
+ * {@link QueryReferenceController}.
*
* @author margaretha
*
@@ -158,59 +166,15 @@
return dtos;
}
- /**
- * Only admin and the owner of the query are allowed to
- * delete a query.
- *
- * @param username
- * username
- * @param queryId
- * query id
- * @throws KustvaktException
- */
- @Deprecated
- public void deleteVC (String username, int vcId) throws KustvaktException {
-
- QueryDO vc = queryDao.retrieveQueryById(vcId);
-
- if (vc.getCreatedBy().equals(username) || adminDao.isAdmin(username)) {
-
- if (vc.getType().equals(ResourceType.PUBLISHED)) {
- QueryAccess access =
- accessDao.retrieveHiddenAccess(vcId);
- accessDao.deleteAccess(access, "system");
- userGroupService.deleteAutoHiddenGroup(
- access.getUserGroup().getId(), "system");
- }
- queryDao.deleteQuery(vc);
- }
- else {
- throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
- "Unauthorized operation for user: " + username, username);
- }
- }
-
- /**
- * Only admin and the owner of the virtual corpus are allowed to
- * delete a virtual corpus.
- *
- * @param username
- * username
- * @param queryName
- * virtual corpus name
- * @param createdBy
- * virtual corpus creator
- * @throws KustvaktException
- */
public void deleteQueryByName (String username, String queryName,
- String createdBy) throws KustvaktException {
+ String createdBy, QueryType type) throws KustvaktException {
QueryDO query = queryDao.retrieveQueryByName(queryName, createdBy);
if (query == null) {
String code = createdBy + "/" + queryName;
throw new KustvaktException(StatusCodes.NO_RESOURCE_FOUND,
- "Virtual corpus " + code + " is not found.",
+ "Query " + code + " is not found.",
String.valueOf(code));
}
else if (query.getCreatedBy().equals(username)
@@ -223,7 +187,8 @@
userGroupService.deleteAutoHiddenGroup(
access.getUserGroup().getId(), "system");
}
- if (KrillCollection.cache.get(query.getName()) != null) {
+ if (type.equals(QueryType.VIRTUAL_CORPUS)
+ && KrillCollection.cache.get(query.getName()) != null) {
KrillCollection.cache.remove(query.getName());
}
queryDao.deleteQuery(query);
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
index 2ebd938..4e90699 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
@@ -3,6 +3,7 @@
import java.util.List;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
@@ -60,7 +61,8 @@
/**
* Creates a query reference according to the given Json.
* The query reference creator must be the same as the
- * authenticated username.
+ * authenticated username, except for admins. Admins may create
+ * and update system queries and queries for/of any users.
*
* TODO: In the future, this may also update a query.
*
@@ -69,9 +71,12 @@
* the username of the vc creator, must be the same
* as the authenticated username
* @param qName
- * the vc name
- * @param query a json object describing the query and its properties
- * @return
+ * the vc name
+ * @param query
+ * a json object describing the query and its
+ * properties
+ * @return HTTP Status 201 Created when creating a new query, or 204
+ * No Content when updating an existing query.
* @throws KustvaktException
*/
@PUT
@@ -130,35 +135,35 @@
}
/**
- * Only the VC owner and system admins can delete VC. VCA admins
- * can delete VC-accesses e.g. of project VC, but not the VC
- * themselves.
+ * Only the query owner and system admins can delete queries.
+ * Query access admins can delete query-accesses e.g. of project
+ * queries, but not the queries themselves.
*
* @param securityContext
* @param createdBy
- * vc creator
- * @param vcName
- * vc name
+ * query creator
+ * @param qName
+ * query name
* @return HTTP status 200, if successful
*/
- /*
+
@DELETE
- @Path("~{createdBy}/{vcName}")
+ @Path("~{createdBy}/{qName}")
public Response deleteVCByName (@Context SecurityContext securityContext,
@PathParam("createdBy") String createdBy,
- @PathParam("vcName") String vcName) {
+ @PathParam("qName") String qName) {
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
scopeService.verifyScope(context, OAuth2Scope.DELETE_VC);
- service.deleteVCByName(context.getUsername(), vcName, createdBy);
+ service.deleteQueryByName(context.getUsername(), qName, createdBy,
+ QueryType.QUERY);
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
}
return Response.ok().build();
};
- */
/**
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index 8f74282..eecb1b3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -71,21 +71,27 @@
private OAuth2ScopeService scopeService;
/**
- * Updates a vc according to the given VirtualCorpusJson, if the
- * VC exists, otherwise creates a new VC with the given VC creator
- * and VC name specified as the path parameters. The vc creator
- * must be the same as the authenticated username.
+ * Creates a new VC with the given VC creator and VC name
+ * specified as the path parameters. If a VC with the same name
+ * and creator exists, the VC will be updated instead.
*
* VC name cannot be updated.
*
+ * The VC creator must be the same as the authenticated username,
+ * except for admins. Admins can create or update system VC as
+ * well as VC for any users.
+ *
+ *
* @param securityContext
* @param vcCreator
* the username of the vc creator, must be the same
- * as the authenticated username
+ * as the authenticated username, except admins
* @param vcName
- * the vc name
- * @param vc a json object describing the VC
- * @return
+ * the vc name
+ * @param vc
+ * a json object describing the VC
+ * @return HTTP Status 201 Created when creating a new VC, or 204
+ * No Content when updating an existing VC.
* @throws KustvaktException
*/
@PUT
@@ -259,7 +265,8 @@
(TokenContext) securityContext.getUserPrincipal();
try {
scopeService.verifyScope(context, OAuth2Scope.DELETE_VC);
- service.deleteQueryByName(context.getUsername(), vcName, createdBy);
+ service.deleteQueryByName(context.getUsername(), vcName, createdBy,
+ QueryType.VIRTUAL_CORPUS);
}
catch (KustvaktException e) {
throw kustvaktResponseHandler.throwit(e);
diff --git a/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java b/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java
index 529efbd..1185bae 100644
--- a/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java
@@ -1,25 +1,25 @@
package de.ids_mannheim.korap.service;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import java.util.List;
-import org.junit.Rule;
+import org.junit.Assert;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import de.ids_mannheim.korap.constant.QueryType;
-import de.ids_mannheim.korap.constant.UserGroupStatus;
import de.ids_mannheim.korap.constant.ResourceType;
+import de.ids_mannheim.korap.constant.UserGroupStatus;
import de.ids_mannheim.korap.dto.QueryAccessDto;
import de.ids_mannheim.korap.dto.QueryDto;
-import de.ids_mannheim.korap.entity.UserGroup;
import de.ids_mannheim.korap.entity.QueryDO;
+import de.ids_mannheim.korap.entity.UserGroup;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.web.input.QueryJson;
@@ -32,12 +32,9 @@
@Autowired
private UserGroupService groupService;
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
@Test
public void testCreateNonUniqueVC () throws KustvaktException {
- thrown.expect(KustvaktException.class);
+
// EM: message differs depending on the database used
// for testing. The message below is from sqlite.
// thrown.expectMessage("A UNIQUE constraint failed "
@@ -48,7 +45,9 @@
vc.setCorpusQuery("corpusSigle=GOE");
vc.setType(ResourceType.PRIVATE);
vc.setQueryType(QueryType.VIRTUAL_CORPUS);
- vcService.storeQuery(vc, "dory-vc", "dory");
+
+ Assert.assertThrows(KustvaktException.class,
+ () -> vcService.storeQuery(vc, "dory-vc", "dory"));
}
@Test
@@ -77,16 +76,18 @@
assertEquals(UserGroupStatus.HIDDEN, group.getStatus());
//delete vc
- vcService.deleteQueryByName(username, vcName, username);
+ vcService.deleteQueryByName(username, vcName, username,
+ QueryType.VIRTUAL_CORPUS);
// check hidden access
accesses = vcService.listQueryAccessByUsername("admin");
assertEquals(size-1, accesses.size());
// check hidden group
- thrown.expect(KustvaktException.class);
- group = groupService.retrieveUserGroupById(groupId);
- thrown.expectMessage("Group with id "+groupId+" is not found");
+ KustvaktException e = assertThrows(KustvaktException.class,
+ () -> groupService.retrieveUserGroupById(groupId));
+ assertEquals("Group with id " + groupId + " is not found",
+ e.getMessage());
}
@Test
@@ -138,9 +139,10 @@
accesses = vcService.listQueryAccessByUsername("admin");
assertEquals(size - 1, accesses.size());
- thrown.expect(KustvaktException.class);
- group = groupService.retrieveUserGroupById(groupId);
- thrown.expectMessage("Group with id 5 is not found");
+ KustvaktException e = assertThrows(KustvaktException.class,
+ () -> groupService.retrieveUserGroupById(groupId));
+
+ assertEquals("Group with id 5 is not found", e.getMessage());
}
}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/QueryReferenceControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/QueryReferenceControllerTest.java
index de5b2da..c2279fe 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/QueryReferenceControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/QueryReferenceControllerTest.java
@@ -17,17 +17,21 @@
import de.ids_mannheim.korap.config.SpringJerseyTest;
import de.ids_mannheim.korap.constant.ResourceType;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.user.User.CorpusAccess;
import de.ids_mannheim.korap.utils.JsonUtils;
public class QueryReferenceControllerTest extends SpringJerseyTest {
private String testUser = "qRefControllerTest";
+ private String adminUser = "admin";
@Test
public void testCreatePrivateQuery () throws KustvaktException {
- String json = "{\"type\": \"PRIVATE\"" + ",\"queryType\": \"QUERY\""
- + ",\"queryLanguage\": \"poliqarp\"" + ",\"query\": \"der\"}";
+ String json = "{\"type\": \"PRIVATE\""
+ + ",\"queryType\": \"QUERY\""
+ + ",\"queryLanguage\": \"poliqarp\""
+ + ",\"query\": \"der\"}";
String qName = "new_query";
ClientResponse response = resource().path(API_VERSION).path("query")
@@ -48,6 +52,109 @@
assertEquals("der", node.at("/query").asText());
assertEquals("poliqarp", node.at("/queryLanguage").asText());
assertEquals(CorpusAccess.PUB.name(), node.at("/requiredAccess").asText());
+
+ testDeleteQueryByName(qName, testUser);
+ }
+
+ @Test
+ public void testCreateUserQueryByAdmin () throws KustvaktException {
+ String json = "{\"type\": \"PRIVATE\""
+ + ",\"queryType\": \"QUERY\""
+ + ",\"queryLanguage\": \"poliqarp\""
+ + ",\"query\": \"Sommer\"}";
+
+ String qName = "marlin-query";
+ ClientResponse response = resource().path(API_VERSION).path("query")
+ .path("~marlin").path(qName)
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+ .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+ .entity(json).put(ClientResponse.class);
+
+ assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+ testDeleteQueryByName(qName, "admin");
+ }
+
+ @Test
+ public void testCreateSystemQuery () throws KustvaktException {
+ String json = "{\"type\": \"SYSTEM\""
+ + ",\"queryType\": \"QUERY\""
+ + ",\"queryLanguage\": \"poliqarp\""
+ + ",\"query\": \"Sommer\"}";
+
+ String qName = "system-query";
+ ClientResponse response = resource().path(API_VERSION).path("query")
+ .path("~system").path(qName)
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(adminUser, "pass"))
+ .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+ .entity(json).put(ClientResponse.class);
+
+ assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+ testDeleteQueryByName(qName, "admin");
+ }
+
+ @Test
+ public void testCreateSystemQueryUnauthorized () throws KustvaktException {
+ String json = "{\"type\": \"SYSTEM\""
+ + ",\"queryType\": \"QUERY\""
+ + ",\"queryLanguage\": \"poliqarp\""
+ + ",\"query\": \"Sommer\"}";
+
+ ClientResponse response = resource().path(API_VERSION).path("query")
+ .path("~"+testUser).path("system-query")
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(testUser, "pass"))
+ .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+ .entity(json).put(ClientResponse.class);
+
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+ node.at("/errors/0/0").asInt());
+ assertEquals("Unauthorized operation for user: " + testUser,
+ node.at("/errors/0/1").asText());
+ }
+
+ @Test
+ public void testDeleteQueryUnauthorized () throws KustvaktException {
+ ClientResponse response = resource().path(API_VERSION).path("query")
+ .path("~dory").path("dory-q")
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(testUser, "pass"))
+ .delete(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+ assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+ node.at("/errors/0/0").asInt());
+ assertEquals("Unauthorized operation for user: " + testUser,
+ node.at("/errors/0/1").asText());
+ }
+
+ @Test
+ public void testDeleteNonExistingQuery () throws KustvaktException {
+ ClientResponse response = resource().path(API_VERSION).path("query")
+ .path("~dory").path("non-existing-query")
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue("dory", "pass"))
+ .delete(ClientResponse.class);
+
+ assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+
+ assertEquals(StatusCodes.NO_RESOURCE_FOUND,
+ node.at("/errors/0/0").asInt());
+ assertEquals("Query dory/non-existing-query is not found.",
+ node.at("/errors/0/1").asText());
+ assertEquals("dory/non-existing-query",
+ node.at("/errors/0/2").asText());
}
@Test
@@ -106,4 +213,15 @@
return JsonUtils.readTree(entity);
}
+ private void testDeleteQueryByName (String qName, String username)
+ throws KustvaktException {
+ ClientResponse response = resource().path(API_VERSION).path("query")
+ .path("~" + username).path(qName)
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue(username, "pass"))
+ .delete(ClientResponse.class);
+
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+ }
+
}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
index 4b85b4b..3286c51 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
@@ -455,6 +455,24 @@
public void testCreateSystemVC () throws KustvaktException {
String json = "{\"type\": \"SYSTEM\""
+ ",\"queryType\": \"VIRTUAL_CORPUS\""
+ + ",\"corpusQuery\": \"pubDate since 1820\"}";
+
+ String vcName = "new_system_vc";
+ ClientResponse response = resource().path(API_VERSION).path("vc")
+ .path("~system").path(vcName)
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue("admin", "pass"))
+ .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+ .entity(json).put(ClientResponse.class);
+
+ assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+ testDeleteVC(vcName, "admin");
+ }
+
+ @Test
+ public void testCreateSystemVCUnauthorized () throws KustvaktException {
+ String json = "{\"type\": \"SYSTEM\""
+ + ",\"queryType\": \"VIRTUAL_CORPUS\""
+ ",\"corpusQuery\": \"creationDate since 1820\"}";
ClientResponse response = resource().path(API_VERSION).path("vc")