Introduced query reference rewrite mechanism

Change-Id: If05c5d6b4a1071d72bc7e7293d6130393a00ca87
diff --git a/core/pom.xml b/core/pom.xml
index f8911f4..408b419 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -241,7 +241,7 @@
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
 			<artifactId>Koral</artifactId>
-			<version>[0.36,)</version>
+			<version>[0.37,)</version>
 			<exclusions>
 				<exclusion>
 					<groupId>org.eclipse.jetty</groupId>
@@ -299,7 +299,7 @@
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
 			<artifactId>Krill</artifactId>
-			<version>[0.59.2,)</version>
+			<version>[0.59.3,)</version>
 			<exclusions>
 				<exclusion>
 					<groupId>org.glassfish.jersey.containers</groupId>
diff --git a/full/Changes b/full/Changes
index 49be3dd..091921f 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,6 +1,8 @@
 # version 0.63
 26/10/2020
    - Updated dependency of nimbus-jose-jwt and oauth2-oidc-sdk (diewald)
+29/10/2020
+   - First introduction of a query reference rewrite (diewald)
 
 # version 0.62.4
 24/01/2020
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
new file mode 100644
index 0000000..aab0b77
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
@@ -0,0 +1,110 @@
+package de.ids_mannheim.korap.rewrite;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.rewrite.KoralNode.RewriteIdentifier;
+import de.ids_mannheim.korap.service.QueryReferenceService;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.util.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * Rewrites query reference with the corresponding koral
+ * query describing the actual query fragment.
+ * 
+ * Based on VirtualCorpusRewrite. 
+ *
+ * @author diewald, margaretha
+ *
+ */
+@Component
+public class QueryReferenceRewrite implements RewriteTask.RewriteQuery {
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    @Autowired
+    private QueryReferenceService refService;
+
+    @Override
+    public KoralNode rewriteQuery (KoralNode node,
+                                   KustvaktConfiguration config,
+                                   User user) throws KustvaktException {
+        if (node.has("query")) {
+            node = node.at("/query");
+            findQueryRef(user.getUsername(), node);
+        }
+        return node;
+    }
+
+    private void findQueryRef (String username, KoralNode koralNode)
+        throws KustvaktException {
+        if (koralNode.has("@type")
+            && koralNode.get("@type").equals("koral:queryRef")) {
+            if (!koralNode.has("ref")) {
+                throw new KustvaktException(
+                    StatusCodes.MISSING_QUERY_REFERENCE,
+                    "ref is not found"
+                    );
+            }
+            else {
+                String queryRefName = koralNode.get("ref");
+                String queryRefOwner = "system";
+                boolean ownerExist = false;
+                if (queryRefName.contains("/")) {
+                    String[] names = queryRefName.split("/");
+                    if (names.length == 2) {
+                        queryRefOwner = names[0];
+                        queryRefName = names[1];
+                        ownerExist = true;
+                    }
+                }
+
+                JsonNode qref = refService.searchQueryByName(
+                    username,
+                    queryRefName,
+                    queryRefOwner);
+
+                rewriteQuery(qref,koralNode);
+            }
+        }
+        
+        else if (koralNode.has("operands")) {
+            KoralNode operands = koralNode.at("/operands");
+        
+            for (int i = 0; i < operands.size(); i++) {
+                KoralNode operand = operands.get(i);
+                this.findQueryRef(username, operand);
+                operand.buildRewrites();
+            }
+        }
+    }
+
+
+    private void removeOwner (String koralQuery,
+                              String queryRefOwner,
+                              KoralNode koralNode) throws KustvaktException {
+        JsonNode jsonNode = koralNode.rawNode();
+        String ref = jsonNode.at("/ref").asText();
+        koralNode.remove("ref", new RewriteIdentifier("ref", ref));
+
+        ref = ref.substring(queryRefOwner.length() + 1, ref.length());
+        koralNode.set("ref", ref, new RewriteIdentifier("ref", ref));
+    }
+
+    private void rewriteQuery (JsonNode qref, KoralNode koralNode)
+        throws KustvaktException {
+        JsonNode jsonNode = koralNode.rawNode();
+        koralNode.remove("@type",
+                new RewriteIdentifier("@type", jsonNode.at("/@type").asText()));
+        koralNode.remove("ref",
+                new RewriteIdentifier("ref", jsonNode.at("/ref").asText()));
+        koralNode.setAll((ObjectNode) qref);
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java b/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
new file mode 100644
index 0000000..eb99f68
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
@@ -0,0 +1,34 @@
+package de.ids_mannheim.korap.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * @author diewald
+ */
+@Service
+public class QueryReferenceService {
+
+
+    public JsonNode searchQueryByName (String username,
+                                       String refName,
+                                       String createdBy) throws KustvaktException {
+
+        String refCode = createdBy + "/" + refName;
+
+        if (refCode.equals("system/emptyToken")) {
+            return JsonUtils.readTree("{\"@type\":\"koral:token\"}");
+        };
+
+        throw new KustvaktException(
+            StatusCodes.NO_RESOURCE_FOUND,
+            "Query reference " + refCode + " is not found.",
+            String.valueOf(refCode));
+    }
+};
diff --git a/full/src/main/resources/default-config.xml b/full/src/main/resources/default-config.xml
index bd25718..c1da94b 100644
--- a/full/src/main/resources/default-config.xml
+++ b/full/src/main/resources/default-config.xml
@@ -220,12 +220,14 @@
 	<bean id="foundryRewrite" class="de.ids_mannheim.korap.rewrite.FoundryRewrite"/>
 	<bean id="collectionRewrite" class="de.ids_mannheim.korap.rewrite.CollectionRewrite"/>
 	<bean id="virtualCorpusRewrite" class="de.ids_mannheim.korap.rewrite.VirtualCorpusRewrite"/>
+ 	<bean id="queryReferenceRewrite" class="de.ids_mannheim.korap.rewrite.QueryReferenceRewrite"/>
 	
 	<util:list id="rewriteTasks"
 		value-type="de.ids_mannheim.korap.rewrite.RewriteTask">
 		<ref bean="foundryRewrite" />
 		<ref bean="collectionRewrite" />
 		<ref bean="virtualCorpusRewrite" />
+    <ref bean="queryReferenceRewrite" />
 	</util:list>
 	
 	<bean id="rewriteHandler" class="de.ids_mannheim.korap.rewrite.RewriteHandler">
@@ -389,4 +391,4 @@
 			</props>
 		</constructor-arg>
 	</bean>
-</beans>
\ No newline at end of file
+</beans>
diff --git a/full/src/test/java/de/ids_mannheim/korap/rewrite/QueryRewriteTest.java b/full/src/test/java/de/ids_mannheim/korap/rewrite/QueryRewriteTest.java
new file mode 100644
index 0000000..5cae6bc
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/rewrite/QueryRewriteTest.java
@@ -0,0 +1,55 @@
+package de.ids_mannheim.korap.rewrite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
+import com.sun.jersey.api.client.ClientResponse;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.util.QueryException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * @author diewald
+ *
+ */
+public class QueryRewriteTest extends SpringJerseyTest {
+
+    @Test
+    public void testRewriteRefNotFound ()
+            throws KustvaktException, Exception {
+
+        ClientResponse response = resource().path(API_VERSION).path("search")
+            .queryParam("q", "[orth=der]{%23examplequery} Baum")
+            .queryParam("ql", "poliqarp")
+            .get(ClientResponse.class);
+
+        String ent = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(node.at("/errors/0/1").asText(), "Query reference system/examplequery is not found.");
+    }
+
+    @Test
+    public void testRewriteRefRewrite ()
+            throws KustvaktException, Exception {
+
+        ClientResponse response = resource().path(API_VERSION).path("search")
+            .queryParam("q", "[orth=der]{%23system/emptyToken} Baum")
+            .queryParam("ql", "poliqarp")
+            .get(ClientResponse.class);
+
+        String ent = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals("koral:token", node.at("/query/operands/1/@type").asText());
+        assertEquals("@type(koral:queryRef)", node.at("/query/operands/1/rewrites/0/scope").asText());
+    }
+}
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index 82b314e..b9f5e07 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -203,6 +203,7 @@
 	<bean id="collectionCleanRewrite" class="de.ids_mannheim.korap.rewrite.CollectionCleanRewrite"/>
 	<bean id="virtualCorpusRewrite" class="de.ids_mannheim.korap.rewrite.VirtualCorpusRewrite"/>
 	<bean id="collectionConstraint" class="de.ids_mannheim.korap.rewrite.CollectionConstraint"/>
+ 	<bean id="queryReferenceRewrite" class="de.ids_mannheim.korap.rewrite.QueryReferenceRewrite"/>
 	
 	<util:list id="rewriteTasks"
 		value-type="de.ids_mannheim.korap.rewrite.RewriteTask">
@@ -211,6 +212,7 @@
 		<ref bean="foundryRewrite" />
 		<ref bean="collectionRewrite" />
 		<ref bean="virtualCorpusRewrite" />
+		<ref bean="queryReferenceRewrite" />
 	</util:list>
 	
 	<bean id="rewriteHandler" class="de.ids_mannheim.korap.rewrite.RewriteHandler">
@@ -375,4 +377,4 @@
 			</props>
 		</constructor-arg>
 	</bean>
-</beans>
\ No newline at end of file
+</beans>