This is an automated email from the ASF dual-hosted git repository. andy pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit fa17b52c172d3ed97b48b55624551972d82e0650 Author: Andy Seaborne <[email protected]> AuthorDate: Thu Nov 7 21:14:18 2024 +0000 Fix: Servlet SPARQL_Update: POST using a query string --- .../java/org/apache/jena/riot/web/HttpNames.java | 13 ++++--- .../apache/jena/fuseki/servlets/SPARQL_Update.java | 27 +++++++++++--- .../jena/sparql/exec/http/TestUpdateExecHTTP.java | 43 ++++++++++++++++++++-- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java b/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java index cc68dfd302..c8460ae1c9 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java @@ -64,18 +64,19 @@ public class HttpNames public static final String graphTargetDefault = "default" ; public static final String graphTargetUnion = "union" ; - public static final String paramUpdate = "update" ; - public static final String paramRequest = "request" ; - public static final String paramUsingGraphURI = "using-graph-uri" ; - public static final String paramUsingNamedGraphURI = "using-named-graph-uri" ; - - // SPARQL parameter names + // SPARQL query parameter names public static final String paramQuery = "query" ; public static final String paramQueryRef = "query-ref" ; public static final String paramDefaultGraphURI = "default-graph-uri" ; public static final String paramNamedGraphURI = "named-graph-uri" ; public static final String paramTarget = "target" ; + // SPARQL Update parameter names + public static final String paramUpdate = "update" ; + public static final String paramRequest = "request" ; // Alternative name. + public static final String paramUsingGraphURI = "using-graph-uri" ; + public static final String paramUsingNamedGraphURI = "using-named-graph-uri" ; + // Jena parameter names (SPARQL protocol extensions) public static final String paramStyleSheet = "stylesheet" ; public static final String paramLang = "lang" ; diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java index 8c50cddfd5..17daa8072e 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java @@ -52,6 +52,7 @@ import org.apache.jena.irix.IRIxResolver; import org.apache.jena.query.QueryBuildException; import org.apache.jena.query.QueryParseException; import org.apache.jena.query.Syntax; +import org.apache.jena.riot.WebContent; import org.apache.jena.riot.web.HttpNames; import org.apache.jena.shared.OperationDeniedException; import org.apache.jena.sparql.engine.http.QueryExceptionHTTP; @@ -98,9 +99,7 @@ public class SPARQL_Update extends ActionService @Override public void execute(HttpAction action) { - ContentType ct = ActionLib.getContentType(action); - if ( ct == null ) - ct = ctSPARQLUpdate; + ContentType ct = updateContentType(action); if ( matchContentType(ctSPARQLUpdate, ct) ) { executeBody(action); @@ -127,9 +126,7 @@ public class SPARQL_Update extends ActionService if ( ! HttpNames.METHOD_POST.equalsIgnoreCase(action.getRequestMethod()) && ! HttpNames.METHOD_PATCH.equalsIgnoreCase(action.getRequestMethod()) ) ServletOps.errorMethodNotAllowed("SPARQL Update : use POST or PATCH"); - ContentType ct = ActionLib.getContentType(action); - if ( ct == null ) - ct = ctSPARQLUpdate; + ContentType ct = updateContentType(action); if ( matchContentType(ctSPARQLUpdate, ct) ) { String charset = action.getRequestCharacterEncoding(); @@ -261,6 +258,24 @@ public class SPARQL_Update extends ActionService } finally { action.endWrite(); } } + /** + * Content type, with a default depending on whether it looks like a HTMLform + * using the query string. + */ + private static ContentType updateContentType(HttpAction action) { + ContentType ct = ActionLib.getContentType(action); + if ( ct != null ) + return ct; + + // No content type header. Not covered by the spec which has MUST be + // HTML form application/x-www-form-urlencoded (query string) or application sparql-update. + // However, it is convenient to deal with the "no content header + queryString update=" case. + // If there is a query string, "request=" or "update=" treat as HTML form. + if ( action.getRequestParameter(paramUpdate) != null || action.getRequestParameter(paramRequest) != null ) + return WebContent.ctHTMLForm; + return ctSPARQLUpdate; + } + /* [It is an error to supply the using-graph-uri or using-named-graph-uri parameters * when using this protocol to convey a SPARQL 1.1 Update request that contains an * operation that uses the USING, USING NAMED, or WITH clause.] diff --git a/jena-integration-tests/src/test/java/org/apache/jena/sparql/exec/http/TestUpdateExecHTTP.java b/jena-integration-tests/src/test/java/org/apache/jena/sparql/exec/http/TestUpdateExecHTTP.java index 782d4d91a8..5d72bfdfd6 100644 --- a/jena-integration-tests/src/test/java/org/apache/jena/sparql/exec/http/TestUpdateExecHTTP.java +++ b/jena-integration-tests/src/test/java/org/apache/jena/sparql/exec/http/TestUpdateExecHTTP.java @@ -19,16 +19,21 @@ package org.apache.jena.sparql.exec.http; import static org.apache.jena.sparql.sse.SSE.parseQuad; -import static org.junit.Assert.assertTrue; import org.apache.jena.atlas.logging.LogCtl; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.main.FusekiServer; +import org.apache.jena.graph.Graph; +import org.apache.jena.http.HttpLib; +import org.apache.jena.http.HttpOp; import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.DatasetGraphFactory; import org.apache.jena.sparql.core.Quad; import org.apache.jena.update.UpdateFactory; import org.apache.jena.update.UpdateRequest; + +import static org.junit.Assert.*; + import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -76,7 +81,7 @@ public class TestUpdateExecHTTP { private static String service() { return dsURL; } private static String serviceQuery() { return dsURL+"/query"; } - + private static String serviceUpdate() { return dsURL+"/update"; } @Test public void update_1() { UpdateExecHTTP uExec = UpdateExecHTTP.service(service()) @@ -110,9 +115,41 @@ public class TestUpdateExecHTTP { } } + // Alternative to UpdateExecHTTP -- convenient update from other languages + // Fuseki treats: + // POST ...?request=... + // POST ...?update=... + // as an update. + @Test public void update_POST_queryString_1() { + String update1 = "INSERT DATA { <x:s> <x:p> 567 }"; + String queryString1 = "update="+HttpLib.urlEncodeQueryString(update1); + + String update2 = "CLEAR DEFAULT"; + String queryString2 = "update="+HttpLib.urlEncodeQueryString(update2); + + // Multiple operation endpoint. + HttpOp.httpPost(service()+"?"+queryString1); + Graph g1 = GSP.service(service()).defaultGraph().GET(); + assertEquals(1, g1.size()); + + HttpOp.httpPost(service()+"?"+queryString2); + Graph g2 = GSP.service(service()).defaultGraph().GET(); + assertEquals(0, g2.size()); + } + + @Test public void update_POST_queryString_2() { + String update = "INSERT DATA { <x:s> <x:p> 567 }"; + String queryString = "request="+HttpLib.urlEncodeQueryString(update); + + // Update endpoint. + HttpOp.httpPost(serviceUpdate()+"?"+queryString); + + Graph g = GSP.service(service()).defaultGraph().GET(); + assertEquals(1, g.size()); + } + // ?user-graph-uri= and ?using-named-graph-uri only apply to the WHERE clause of // an update. - @Test public void update_using_1() { try { update_using_1_test();
