[ https://issues.apache.org/jira/browse/SLING-7509?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16378704#comment-16378704 ]
ASF GitHub Bot commented on SLING-7509: --------------------------------------- dulvac commented on a change in pull request #5: SLING-7509 - Add QueryClient URL: https://github.com/apache/sling-org-apache-sling-testing-clients/pull/5#discussion_r170932869 ########## File path: src/main/java/org/apache/sling/testing/clients/query/QueryClient.java ########## @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.sling.testing.clients.query; + +import org.apache.http.NameValuePair; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.sling.testing.clients.ClientException; +import org.apache.sling.testing.clients.SlingClient; +import org.apache.sling.testing.clients.SlingClientConfig; +import org.apache.sling.testing.clients.SlingHttpResponse; +import org.apache.sling.testing.clients.osgi.OsgiConsoleClient; +import org.apache.sling.testing.clients.util.JsonUtils; +import org.apache.sling.testing.clients.util.URLParameterBuilder; +import org.codehaus.jackson.JsonNode; +import org.ops4j.pax.tinybundles.core.TinyBundles; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.util.List; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.apache.http.HttpStatus.SC_NOT_FOUND; +import static org.apache.http.HttpStatus.SC_OK; +import static org.apache.sling.testing.clients.util.ResourceUtil.getResourceAsStream; + +/** + * <p>Sling client for performing oak queries.</p> + * + * <p>Uses a custom query servlet {@code testQuery.json.jsp} to execute the query on the server + * and return the results as a json. If the servlet is not yet present, it automatically + * installs it and creates the corresponding nodes</p> + * + * <p>Paths created in jcr:<br> + * - {@value JSP_PATH}, created by the bundle<br> + * - {@value QUERY_NODE}, with {@code resourceType}={@value RESOURCE_TYPE} + * </p> + * + * <p>The servlet is not automatically uninstalled to avoid too much noise on the instance. + * The caller should take care of it, if needed, by calling {@link #uninstallServlet()}</p> + */ +public class QueryClient extends SlingClient { + + /** + * Query types, as defined in {@code org.apache.jackrabbit.oak.query.QueryEngineImpl} + */ + public enum QueryType { + SQL2("JCR-SQL2"), + SQL("sql"), + XPATH("xpath"), + JQOM("JCR-JQOM"); + + private final String name; + + QueryType(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + private static final Logger LOG = LoggerFactory.getLogger(QueryClient.class); + + private static final String BUNDLE_BSN = "org.apache.sling.testing.clients.query"; + private static final String BUNDLE_NAME = "Query servlet for testing"; + private static final String BUNDLE_VERSION = "1.0.0"; + private static final String BUNDLE_FILE = "testquery.bundle."; + + private static final String JSP_FILE = "/org/apache/sling/testing/clients/query/testQuery.json.jsp"; + private static final String RESOURCE_TYPE = "testQuery"; + private static final String COMPONENT_PATH = "apps/" + RESOURCE_TYPE; + private static final String JSP_PATH = COMPONENT_PATH + "/" + RESOURCE_TYPE + ".json.jsp"; + private static final String QUERY_NODE = "/content/testquery"; + private static final String QUERY_PATH = QUERY_NODE + ".json"; + + /** + * Constructor used by adaptTo + * @param http underlying HttpClient + * @param config config state + * @throws ClientException if the client cannot be created + */ + public QueryClient(CloseableHttpClient http, SlingClientConfig config) throws ClientException { + super(http, config); + } + + /** + * Convenience constructor + * @param url host url + * @param user username + * @param password password + * @throws ClientException if the client cannot be constructed + */ + public QueryClient(URI url, String user, String password) throws ClientException { + super(url, user, password); + } + + /** + * Execute a query on the server and return the results as a json + * + * @param query query to be executed + * @param type type of the query + * @return the results in json as exported by {@code testQuery.json.jsp} + * @throws ClientException if the request failed to execute + */ + public JsonNode doQuery(final String query, final QueryType type) throws ClientException { + return doQuery(query, type, true); + } + + /** + * Execute a query on the server and return only the number of rows in the result + * + * @param query query to be executed + * @param type type of the query + * @return total results returned by the query + * @throws ClientException if the request failed to execute + */ + public long doCount(final String query, final QueryType type) throws ClientException { + return doQuery(query, type, false).get("total").getLongValue(); + } + + protected JsonNode doQuery(final String query, final QueryType type, final boolean showResults) + throws ClientException { + + List<NameValuePair> params = URLParameterBuilder.create() + .add("query", query) + .add("type", type.toString()) + .add("showresults", Boolean.toString(showResults)) + .getList(); + + try { + // try optimistically to execute the query + SlingHttpResponse response = this.doGet(QUERY_PATH, params, SC_OK); + return JsonUtils.getJsonNodeFromString(response.getContent()); + // TODO check the json is a query result, not a default representation of the node + } catch (ClientException e) { + if (e.getHttpStatusCode() == SC_NOT_FOUND) { + LOG.info("Failed to execute the first search, maybe because the servlet was not yet installed"); + installServlet(); + // retry the same query + SlingHttpResponse response = this.doGet(QUERY_PATH, params, SC_OK); + return JsonUtils.getJsonNodeFromString(response.getContent()); + } else { + throw e; + } + } + } + + /** + * <p>Install the servlet to be able to perform queries.</p> + * + * <p>By default, methods of this client automatically call this, so there is no + * need to explicitly call from outside</p> + * + * @throws ClientException if the installation failed + */ + public void installServlet() throws ClientException { + InputStream jspStream = getResourceAsStream(JSP_FILE); + + InputStream bundleStream = TinyBundles.bundle() + .set("Bundle-SymbolicName", BUNDLE_BSN) + .set("Bundle-Version", BUNDLE_VERSION) + .set("Bundle-Name", BUNDLE_NAME) + .set("Sling-Bundle-Resources", "/" + COMPONENT_PATH) + .add(JSP_PATH, jspStream) + .build(); + + File bundleFile; + try { + bundleFile = File.createTempFile(BUNDLE_FILE, null); + Files.copy(bundleStream, bundleFile.toPath(), REPLACE_EXISTING); + } catch (IOException e) { + throw new ClientException("Failed to install the query servlet bundle", e); + } + + adaptTo(OsgiConsoleClient.class).installBundle(bundleFile, true); + + createNode(QUERY_NODE, "nt:unstructured"); + setPropertyString(QUERY_NODE, "sling:resourceType", RESOURCE_TYPE); + + LOG.info("query servlet installed at {}", getUrl(QUERY_PATH)); + } + + /** + * Delete all the resources created by {@link #installServlet()} + * + * @throws ClientException if any of the resources failed to uninstall + */ + public void uninstallServlet() throws ClientException { Review comment: Same here ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org > Add QueryClient > --------------- > > Key: SLING-7509 > URL: https://issues.apache.org/jira/browse/SLING-7509 > Project: Sling > Issue Type: New Feature > Components: Apache Sling Testing Clients > Reporter: Valentin Olteanu > Priority: Major > > Currently, there is no way to run queries in sling using the clients. This is > needed in several tests to search content and assert the effects of a feature. > > The solution proposed in > [https://github.com/apache/sling-org-apache-sling-testing-clients/pull/5] > * installs a custom query servlet > * runs the query in any of the supported format > * returns results as json -- This message was sent by Atlassian JIRA (v7.6.3#76005)