diff --git a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/ApiConnection.java b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/ApiConnection.java index 8e2184249..a6590611d 100644 --- a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/ApiConnection.java +++ b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/ApiConnection.java @@ -27,6 +27,8 @@ import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.wikidata.wdtk.util.WebResourceFetcherImpl; +import org.wikidata.wdtk.wikibaseapi.apierrors.MaxlagErrorException; import org.wikidata.wdtk.wikibaseapi.apierrors.AssertUserFailedException; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorHandler; @@ -414,9 +416,16 @@ private void buildClient() { protected void checkErrors(JsonNode root) throws MediaWikiApiErrorException { if (root.has("error")) { JsonNode errorNode = root.path("error"); - MediaWikiApiErrorHandler.throwMediaWikiApiErrorException(errorNode - .path("code").asText("UNKNOWN"), errorNode.path("info") - .asText("No details provided")); + String code = errorNode.path("code").asText("UNKNOWN"); + String info = errorNode.path("info").asText("No details provided"); + // Special case for the maxlag error since we also want to return + // the lag value in the exception thrown + if (errorNode.has("lag") && MediaWikiApiErrorHandler.ERROR_MAXLAG.equals(code)) { + double lag = errorNode.path("lag").asDouble(); + throw new MaxlagErrorException(info, lag); + } else { + MediaWikiApiErrorHandler.throwMediaWikiApiErrorException(code, info); + } } } diff --git a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbEditingAction.java b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbEditingAction.java index 1b0160be6..52fcf0a4c 100644 --- a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbEditingAction.java +++ b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbEditingAction.java @@ -894,4 +894,25 @@ public void setMaxLagBackOffFactor(double value) { maxLagBackOffFactor = value; } + /** + * Retrieves the current lag from the target site, by making an API call. + * + * @throws MediaWikiApiErrorException + * when an unexpected MediaWiki API error happened (not the spurious + * one normally returned by MediaWiki when retrieving lag). + * @throws IOException + * when communication with the server failed. + * + */ + public double getCurrentLag() throws IOException, MediaWikiApiErrorException { + Map parameters = new HashMap<>(); + parameters.put("action", "query"); + parameters.put("maxlag", "-1"); + try { + this.connection.sendJsonRequest("POST", parameters); + } catch (MaxlagErrorException e) { + return e.getLag(); + } + throw new IllegalStateException("MediaWiki did not return any maxlag value"); + } } diff --git a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/apierrors/MaxlagErrorException.java b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/apierrors/MaxlagErrorException.java index 675526958..5a2b0c8f4 100644 --- a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/apierrors/MaxlagErrorException.java +++ b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/apierrors/MaxlagErrorException.java @@ -32,6 +32,8 @@ public class MaxlagErrorException extends MediaWikiApiErrorException { private static final long serialVersionUID = -4013361654647685959L; + + protected double lag = 0; /** * Creates a new exception. @@ -43,5 +45,30 @@ public class MaxlagErrorException extends MediaWikiApiErrorException { public MaxlagErrorException(String errorMessage) { super(MediaWikiApiErrorHandler.ERROR_MAXLAG, errorMessage); } - + + /** + * Creates an exception which also stores the lag announced by the server. + * + * @param errorMessage + * the error message reported by MediaWiki, or any other + * meaningful message for the user + * @param lag + * the value of the reported lag, in seconds + */ + public MaxlagErrorException(String errorMessage, double lag) { + super(MediaWikiApiErrorHandler.ERROR_MAXLAG, errorMessage); + this.lag = lag; + } + + /** + * Retrieves the amount of lag announced by the server when this + * error was emitted. May return 0 if the lag was not extracted from + * the server response. + * + * @return + * the lag on the target server + */ + public double getLag() { + return lag; + } } diff --git a/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/BasicApiConnectionTest.java b/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/BasicApiConnectionTest.java index 8be3eba73..d977d6e61 100644 --- a/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/BasicApiConnectionTest.java +++ b/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/BasicApiConnectionTest.java @@ -36,6 +36,7 @@ import org.junit.*; import org.wikidata.wdtk.testing.MockStringContentFactory; import org.wikidata.wdtk.wikibaseapi.apierrors.AssertUserFailedException; +import org.wikidata.wdtk.wikibaseapi.apierrors.MaxlagErrorException; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; import com.fasterxml.jackson.databind.JsonNode; @@ -232,6 +233,18 @@ public void testErrors() throws IOException, root = mapper.readTree(path.openStream()); connection.checkErrors(root); } + + @Test + public void testMaxlagError() throws IOException, MediaWikiApiErrorException { + JsonNode root; + URL path = this.getClass().getResource("/error-maxlag-full.json"); + root = mapper.readTree(path.openStream()); + try { + connection.checkErrors(root); + } catch(MaxlagErrorException e) { + assertEquals(3.45, e.getLag(), 0.001); + } + } @Test public void testClearCookies() throws LoginFailedException, IOException, MediaWikiApiErrorException { diff --git a/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/WbEditingActionTest.java b/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/WbEditingActionTest.java index a57af7ce9..cc90e197a 100644 --- a/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/WbEditingActionTest.java +++ b/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/WbEditingActionTest.java @@ -1,5 +1,7 @@ package org.wikidata.wdtk.wikibaseapi; +import static org.junit.Assert.assertEquals; + /* * #%L * Wikidata Toolkit Wikibase API @@ -191,5 +193,22 @@ public void testNoTask() throws IOException, MediaWikiApiErrorException { new MockBasicApiConnection(), Datamodel.SITE_WIKIDATA); weea.wbEditEntity(null, null, null, null, "{}", false, false, 0, null, null); } + + @Test + public void testGetLag() throws IOException, MediaWikiApiErrorException { + MockBasicApiConnection con = new MockBasicApiConnection(); + Map params = new HashMap<>(); + params.put("action", "query"); + params.put("maxlag", "-1"); + params.put("format", "json"); + con.setWebResourceFromPath(params, this.getClass(), + "/error-maxlag-full.json", + CompressionType.NONE); + + WbEditingAction weea = new WbEditingAction( + con, Datamodel.SITE_WIKIDATA); + double lag = weea.getCurrentLag(); + assertEquals(3.45, lag, 0.001); + } } diff --git a/wdtk-wikibaseapi/src/test/resources/error-maxlag-full.json b/wdtk-wikibaseapi/src/test/resources/error-maxlag-full.json new file mode 100644 index 000000000..24746b101 --- /dev/null +++ b/wdtk-wikibaseapi/src/test/resources/error-maxlag-full.json @@ -0,0 +1,12 @@ +{ + "error": { + "code": "maxlag", + "info": "Waiting for all: 3.45 seconds lagged.", + "host": "all", + "lag": 3.45, + "type": "wikibase-queryservice", + "queryserviceLag": 626, + "*": "See https://www.wikidata.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes." + }, + "servedby": "mw1280" +}