From 2398042ed572b736f39945849892c17be397c243 Mon Sep 17 00:00:00 2001 From: lsitu Date: Thu, 10 Jul 2014 16:23:51 -0700 Subject: [PATCH] Add form request (application/x-www-form-urlencoded) support for sparql end point - Refactor the rest api codes for sparql Resolves: https://www.pivotaltracker.com/story/show/72619228 --- .../fcrepo/transform/http/FedoraSparql.java | 54 +++++++- .../transform/http/FedoraSparqlIT.java | 129 ++++++++++++++++++ 2 files changed, 176 insertions(+), 7 deletions(-) diff --git a/fcrepo-transform/src/main/java/org/fcrepo/transform/http/FedoraSparql.java b/fcrepo-transform/src/main/java/org/fcrepo/transform/http/FedoraSparql.java index 0590222510..ff2f030db7 100644 --- a/fcrepo-transform/src/main/java/org/fcrepo/transform/http/FedoraSparql.java +++ b/fcrepo-transform/src/main/java/org/fcrepo/transform/http/FedoraSparql.java @@ -16,6 +16,7 @@ package org.fcrepo.transform.http; import com.codahale.metrics.annotation.Timed; +import com.google.common.base.Strings; import com.hp.hpl.jena.query.ResultSet; import org.apache.commons.io.IOUtils; import org.apache.velocity.Template; @@ -34,10 +35,10 @@ import org.slf4j.Logger; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; - import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -60,13 +61,15 @@ import static com.google.common.util.concurrent.Futures.addCallback; import static javax.ws.rs.core.MediaType.TEXT_HTML; import static javax.ws.rs.core.Response.ok; +import static javax.ws.rs.core.Response.status; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.jena.riot.WebContent.contentTypeN3; import static org.apache.jena.riot.WebContent.contentTypeNTriples; import static org.apache.jena.riot.WebContent.contentTypeRDFXML; import static org.apache.jena.riot.WebContent.contentTypeResultsBIO; import static org.apache.jena.riot.WebContent.contentTypeResultsJSON; import static org.apache.jena.riot.WebContent.contentTypeResultsXML; -import static org.apache.jena.riot.WebContent.contentTypeSPARQLQuery; +import static org.apache.jena.riot.WebContent.contentTypeHTMLForm; import static org.apache.jena.riot.WebContent.contentTypeSSE; import static org.apache.jena.riot.WebContent.contentTypeTextCSV; import static org.apache.jena.riot.WebContent.contentTypeTextPlain; @@ -82,6 +85,7 @@ * Primitive SPARQL JAX-RS endpoint * * @author cabeer + * @author lsitu */ @Component @Scope("prototype") @@ -146,7 +150,6 @@ public void write(final OutputStream output) throws IOException { * @throws RepositoryException */ @POST - @Consumes({contentTypeSPARQLQuery}) @Produces({contentTypeTextTSV, contentTypeTextCSV, contentTypeSSE, contentTypeTextPlain, contentTypeResultsJSON, contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle, @@ -162,17 +165,54 @@ public Response runSparqlQuery(final InputStream requestBodyStream, final String sparqlQuery = IOUtils.toString(requestBodyStream); - LOGGER.trace("Running SPARQL query: {}", sparqlQuery); + return rexecSparql(sparqlQuery, bestPossibleResponse, graphSubjects); + } + + /** + * Execute a SPARQL query against the JCR index + * @param query + * @param request + * @param uriInfo + * @return SPARQL query results + * @throws IOException + * @throws RepositoryException + */ + @POST + @Consumes({contentTypeHTMLForm}) + @Produces({contentTypeTextTSV, contentTypeTextCSV, contentTypeSSE, + contentTypeTextPlain, contentTypeResultsJSON, + contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle, + contentTypeN3, contentTypeNTriples, contentTypeRDFXML}) + public Response runSparqlQuery(@FormParam("query") final String query, + @Context final Request request, + @Context final UriInfo uriInfo) + throws IOException, RepositoryException { + + LOGGER.trace("POST SPARQL query with {}: {}", contentTypeHTMLForm, query); + if (Strings.isNullOrEmpty(query)) { + return status(BAD_REQUEST) + .entity("SPARQL must not be null. Please submit a query with parameter 'query'.") + .build(); + } + return rexecSparql(query, + request.selectVariant(POSSIBLE_SPARQL_RDF_VARIANTS), + new HttpIdentifierTranslator(session, FedoraNodes.class, uriInfo)); + } + + private Response rexecSparql(final String sparql, + final Variant bestPossibleResponse, + final IdentifierTranslator graphSubjects) + throws RepositoryException { + LOGGER.trace("Running SPARQL query: {}", sparql); - final JQLConverter jqlConverter = new JQLConverter(session, graphSubjects, sparqlQuery); + final JQLConverter jqlConverter = new JQLConverter(session, graphSubjects, sparql); LOGGER.trace("Converted to JQL query: {}", jqlConverter.getStatement()); final ResultSet resultSet = jqlConverter.execute(); final ResultSetStreamingOutput streamingOutput = - new ResultSetStreamingOutput(resultSet, bestPossibleResponse - .getMediaType()); + new ResultSetStreamingOutput(resultSet, bestPossibleResponse.getMediaType()); addCallback(streamingOutput, new LogoutCallback(session)); diff --git a/fcrepo-transform/src/test/java/org/fcrepo/integration/transform/http/FedoraSparqlIT.java b/fcrepo-transform/src/test/java/org/fcrepo/integration/transform/http/FedoraSparqlIT.java index 8301ba42db..9a7e83d4df 100644 --- a/fcrepo-transform/src/test/java/org/fcrepo/integration/transform/http/FedoraSparqlIT.java +++ b/fcrepo-transform/src/test/java/org/fcrepo/integration/transform/http/FedoraSparqlIT.java @@ -19,10 +19,13 @@ import com.hp.hpl.jena.query.ResultSet; import com.hp.hpl.jena.query.ResultSetFactory; import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.BasicHttpEntity; +import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.fcrepo.integration.AbstractResourceIT; import org.fcrepo.kernel.impl.FedoraResourceImpl; @@ -32,14 +35,19 @@ import org.modeshape.jcr.api.JcrConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; +import static org.apache.jena.riot.WebContent.contentTypeHTMLForm; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFactory; +import javax.ws.rs.core.Response.Status; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -211,4 +219,125 @@ private String getResponseContent(final String sparql) throws IOException { logger.trace("Retrieved sparql feed:\n" + content); return content; } + + + private String getFormRequestResponseContent(final String sparql) + throws IOException { + final HttpPost request = getFormRequest (sparql, "query"); + + final HttpResponse response = client.execute(request); + assertEquals(200, response.getStatusLine().getStatusCode()); + + final String content = EntityUtils.toString(response.getEntity()); + logger.trace("Retrieved sparql feed:\n" + content); + return content; + } + + private HttpPost getFormRequest (final String sparql, final String paramName) + throws UnsupportedEncodingException { + final HttpPost post = new HttpPost(serverAddress + "/fcr:sparql"); + post.addHeader("Content-Type", contentTypeHTMLForm); + final List nvps = new ArrayList<>(); + if (sparql != null) { + nvps.add(new BasicNameValuePair( + paramName != null && paramName.length() > 0 ? paramName : "sparql", sparql)); + } + + final HttpEntity formEntity = new UrlEncodedFormEntity(nvps, "UTF-8"); + post.setEntity(formEntity); + return post; + } + + @Test + public void formRequestShouldWorkWithSimpleProperties() throws IOException { + + final String sparql = "PREFIX dc: " + + "SELECT ?subject WHERE { ?subject dc:title \"xyz\"}"; + + final String content = getFormRequestResponseContent(sparql); + final ResultSet resultSet = ResultSetFactory.fromTSV(IOUtils.toInputStream(content)); + + + assertTrue(resultSet.hasNext()); + + assertEquals("subject", resultSet.getResultVars().get(0)); + + assertEquals(serverAddress + "/abc", resultSet.next().get("subject").toString()); + } + + @Test + public void formRequestShouldWorkWithRdfTypeMixins() throws IOException { + + final String sparql = + "PREFIX dc: SELECT " + + "?subject WHERE { " + + "?subject a . ?subject dc:title \"xyz\"}"; + + final String content = getFormRequestResponseContent(sparql); + final ResultSet resultSet = ResultSetFactory.fromTSV(IOUtils.toInputStream(content)); + + + assertTrue(resultSet.hasNext()); + + assertEquals("subject", resultSet.getResultVars().get(0)); + + assertEquals(serverAddress + "/abc", resultSet.next().get("subject").toString()); + + } + + @Test + public void formRequestShouldWorkWithReferenceProperties() throws IOException { + + final String sparql = + "PREFIX fedorarelsext: SELECT " + + "?subject ?part WHERE { ?subject fedorarelsext:hasPart ?part }"; + + final String content = getFormRequestResponseContent(sparql); + final ResultSet resultSet = ResultSetFactory.fromTSV(IOUtils.toInputStream(content)); + + + assertTrue(resultSet.hasNext()); + + assertEquals("subject", resultSet.getResultVars().get(0)); + + final QuerySolution row = resultSet.next(); + assertEquals(serverAddress + "/abc", row.get("subject").toString()); + assertEquals(serverAddress + "/xyz", row.get("part").toString()); + } + + @Test + public void formRequestShouldWorkWithJoinedQueries() throws IOException { + + final String sparql = "PREFIX fedorarelsext: \n" + + "PREFIX dc: \n" + + "SELECT ?part ?collectionTitle WHERE { ?part fedorarelsext:isPartOf ?collection .\n" + + " ?collection dc:title ?collectionTitle }"; + + final String content = getFormRequestResponseContent(sparql); + final ResultSet resultSet = ResultSetFactory.fromTSV(IOUtils.toInputStream(content)); + + + assertTrue(resultSet.hasNext()); + + assertEquals("part", resultSet.getResultVars().get(0)); + + final QuerySolution row = resultSet.next(); + assertEquals(serverAddress + "/xyz", row.get("part").toString()); + assertEquals("xyz", row.get("collectionTitle").asLiteral().getLexicalForm()); + } + + @Test + public void testBadFormRequest() throws IOException { + + String sparql = ""; + + HttpPost badRequest = getFormRequest(sparql, null); + HttpResponse response = client.execute(badRequest); + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatusLine().getStatusCode()); + + sparql = null; + badRequest = getFormRequest(sparql, null); + response = client.execute(badRequest); + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatusLine().getStatusCode()); + } }