Smalyshev has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/349119 )
Change subject: [WIP] [DNM] Add Mediawiki API service ...................................................................... [WIP] [DNM] Add Mediawiki API service Mediaiwki API is described by a template, which lists inputs and outputs, and is invoked via a service: SERVICE wikibase:mwapi { bd:serviceParam wikibase:api "Categories" . bd:serviceParam :titles "Albert Einstein" . bd:serviceParam :category ?category . bd:serviceParam :title ?title . } Change-Id: If0aa5c213e197f6b20b55092680930eb82d1a0a2 Bug: T148245 --- M blazegraph/pom.xml M blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/WikibaseContextListener.java M blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/geo/GeoBoxService.java A blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/ApiTemplate.java A blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceCall.java A blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceFactory.java M dist/src/script/runBlazegraph.sh M pom.xml M tools/pom.xml M tools/runBlazegraph.sh A tools/src/test/resources/blazegraph/services.json 11 files changed, 713 insertions(+), 11 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/wikidata/query/rdf refs/changes/19/349119/1 diff --git a/blazegraph/pom.xml b/blazegraph/pom.xml index 0ea96cc..4470840 100644 --- a/blazegraph/pom.xml +++ b/blazegraph/pom.xml @@ -20,6 +20,14 @@ <dependencies> <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> <!-- Blazegraph needs http client to run services. --> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> diff --git a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/WikibaseContextListener.java b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/WikibaseContextListener.java index a028935..e0012fc 100644 --- a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/WikibaseContextListener.java +++ b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/WikibaseContextListener.java @@ -20,6 +20,7 @@ import org.wikidata.query.rdf.blazegraph.constraints.WikibaseNowBOp; import org.wikidata.query.rdf.blazegraph.geo.GeoService; import org.wikidata.query.rdf.blazegraph.label.LabelService; +import org.wikidata.query.rdf.blazegraph.mwapi.MWApiServiceFactory; import org.wikidata.query.rdf.common.uri.GeoSparql; import org.wikidata.query.rdf.common.uri.OWL; import org.wikidata.query.rdf.common.uri.Ontology; @@ -83,6 +84,7 @@ reg.setWhitelistEnabled(true); LabelService.register(); GeoService.register(); + MWApiServiceFactory.register(); // Whitelist services we like by default reg.addWhitelistURL(GASService.Options.SERVICE_KEY.toString()); diff --git a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/geo/GeoBoxService.java b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/geo/GeoBoxService.java index a0d4e8f..5d6198b 100644 --- a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/geo/GeoBoxService.java +++ b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/geo/GeoBoxService.java @@ -272,7 +272,7 @@ */ private final BigdataValueFactory vf; - public GeoBoxServiceCall(BigdataServiceCall wrappedCall, TermNode east, + GeoBoxServiceCall(BigdataServiceCall wrappedCall, TermNode east, TermNode west, AbstractTripleStore kb) { this.wrappedCall = wrappedCall; this.east = east; diff --git a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/ApiTemplate.java b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/ApiTemplate.java new file mode 100644 index 0000000..9ac05eb --- /dev/null +++ b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/ApiTemplate.java @@ -0,0 +1,203 @@ +package org.wikidata.query.rdf.blazegraph.mwapi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.bigdata.bop.IVariable; +import com.bigdata.bop.IVariableOrConstant; +import com.bigdata.rdf.sparql.ast.TermNode; +import com.bigdata.rdf.sparql.ast.eval.ServiceParams; +import com.fasterxml.jackson.databind.JsonNode; + +import static org.wikidata.query.rdf.blazegraph.mwapi.MWApiServiceFactory.paramNameToURI; +/** + * This class represents API template. + */ +public final class ApiTemplate { + /** + * Set of fixed API parameters. + */ + private final Map<String, String> fixedParams = new HashMap<>(); + /** + * Set of API parameters that should come from input vars. + * The value is the default. + */ + private final Map<String, String> inputVars = new HashMap<>(); + /** + * Set of API parameters that should be sent to output. + * The value is the XPath to find the value. + */ + private final Map<String, String> outputVars = new HashMap<>(); + /** + * XPath to result items. + */ + private String items; + /** + * Hidden ctor. + * Use fromJSON() to create the object. + */ + private ApiTemplate() {} + + /** + * Create API template from JSON configuration. + * @param json + * @return + */ + public static ApiTemplate fromJSON(JsonNode json) { + ApiTemplate template = new ApiTemplate(); + + // Parse input params + final JsonNode params = json.get("params"); + for (String paramName: (Iterable<String>)() -> params.fieldNames()) { + if (template.fixedParams.containsKey(paramName) + || template.inputVars.containsKey(paramName)) { + throw new IllegalArgumentException( + "Repeated input parameter " + paramName); + } + + JsonNode value = params.get(paramName); + // scalar value means fixed parameter + if (value.isValueNode()) { + template.fixedParams.put(paramName, value.asText()); + } + // otherwise it's a parameter + // TODO: ignoring type for now + if (value.has("default")) { + template.inputVars.put(paramName, value.get("default").asText()); + } else { + template.inputVars.put(paramName, null); + } + } + + // Parse output params + final JsonNode output = json.get("output"); + template.items = output.get("items").asText(); + final JsonNode vars = output.get("vars"); + for (String paramName: (Iterable<String>)() -> vars.fieldNames()) { + if (template.inputVars.containsKey(paramName)) { + throw new IllegalArgumentException("Parameter " + paramName + " declared as both input and output"); + } + template.outputVars.put(paramName, vars.get(paramName).asText()); + } + + return template; + } + + /** + * Get items XPath. + * @return + */ + public String getItemsPath() { + return items; + } + + /** + * Get call fixed parameters. + * @return + */ + public Map<String, String> getFixedParams() { + return fixedParams; + } + + /** + * Find default for this parameter. + * @param name + * @return Default value or null. + */ + public String getInputDefault(String name) { + return inputVars.get(name); + } + + /** + * Create list of bindings from input params to specific variables or constants. + * @param serviceParams Specific invocation params. + * @return + */ + public Map<String, IVariableOrConstant> getInputVars(final ServiceParams serviceParams) { + Map<String, IVariableOrConstant> vars = new HashMap<>(inputVars.size()); + + for (Map.Entry<String, String> entry : inputVars.entrySet()) { + TermNode var = serviceParams.get(paramNameToURI(entry.getKey()), null); + if (var == null) { + if (entry.getValue() == null) { + // Param should have either binding or default + throw new IllegalArgumentException("Parameter " + entry.getKey() + " must be bound"); + } + // If var is null but we have a default, put null there, service call will know + // how to handle it. + vars.put(entry.getKey(), null); + } else { + if (!var.isConstant() && !var.isVariable()) { + // Binding should be constant or var + throw new IllegalArgumentException("Parameter " + entry.getKey() + " must be constant or variable"); + } + vars.put(entry.getKey(), var.getValueExpression()); + } + } + return vars; + } + + /** + * Create map of output variables from template and service params. + * @param serviceParams Specific invocation params. + * @return + */ + public List<OutputVariable> getOutputVars(final ServiceParams serviceParams) { + List<OutputVariable> vars = new ArrayList<>(outputVars.size()); + for (Map.Entry<String, String> entry : outputVars.entrySet()) { + TermNode varNode = serviceParams.get(paramNameToURI(entry.getKey()), null); + if (varNode == null) { + // It's always OK to ignore output vars + continue; + } + if (!varNode.isVariable()) { + throw new IllegalArgumentException("Output parameter " + entry.getKey() + " must be bound to a variable"); + } + IVariable var = (IVariable)varNode.getValueExpression(); + if (var.isAnonymous()) { + // Using anonymous is useless, but we'll let it pass. + continue; + } + vars.add(new OutputVariable(var, entry.getValue())); + } + return vars; + } + + /** + * Variable in the output of the API. + */ + public static class OutputVariable { + /** + * Original Blazegraph var. + */ + private final IVariable var; + /** + * XPath expression to extract value from result. + */ + private final String xpath; + + public OutputVariable(IVariable var, String xpath) { + this.var = var; + this.xpath = xpath; + } + + /** + * Get associated variable. + * @return + */ + public IVariable getVar() { + return var; + } + + /** + * Get associated variable name. + * @return + */ + public String getName() { + return var.getName(); + } + } + +} diff --git a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceCall.java b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceCall.java new file mode 100644 index 0000000..7b0a453 --- /dev/null +++ b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceCall.java @@ -0,0 +1,292 @@ +package org.wikidata.query.rdf.blazegraph.mwapi; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpMethod; +import org.openrdf.model.Literal; +import org.openrdf.model.impl.LiteralImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.wikidata.query.rdf.blazegraph.mwapi.ApiTemplate.OutputVariable; + +import com.bigdata.bop.Constant; +import com.bigdata.bop.IBindingSet; +import com.bigdata.bop.IVariable; +import com.bigdata.bop.IVariableOrConstant; +import com.bigdata.rdf.internal.IV; +import com.bigdata.rdf.internal.VTE; +import com.bigdata.rdf.internal.impl.TermId; +import com.bigdata.rdf.lexicon.LexiconRelation; +import com.bigdata.rdf.sparql.ast.service.BigdataServiceCall; +import com.bigdata.rdf.sparql.ast.service.IServiceOptions; +import com.bigdata.rdf.sparql.ast.service.MockIVReturningServiceCall; + +import cutthecrap.utils.striterators.ICloseableIterator; + +/** + * Instance of API service call. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class MWApiServiceCall implements MockIVReturningServiceCall, BigdataServiceCall { + private static final Logger log = LoggerFactory.getLogger(MWApiServiceCall.class); + /** + * Service call template. + */ + private final ApiTemplate template; + /** + * List of input variable bindings. + */ + private final Map<String, IVariableOrConstant> inputVars; + /** + * List of output variable bindings. + */ + private final List<OutputVariable> outputVars; + /** + * HTTP connection. + */ + private final HttpClient client; + /** + * The LexiconRelation for the TripleStore we're working with. + */ + private final LexiconRelation lexiconRelation; + + MWApiServiceCall(ApiTemplate template, + Map<String, IVariableOrConstant> inputVars, + List<OutputVariable> outputVars, + HttpClient client, + LexiconRelation lexiconRelation + ) { + this.template = template; + this.inputVars = inputVars; + this.outputVars = outputVars; + this.client = client; + this.lexiconRelation = lexiconRelation; + } + + @Override + public IServiceOptions getServiceOptions() { + return MWApiServiceFactory.SERVICE_OPTIONS; + } + + @Override + public ICloseableIterator<IBindingSet> call(IBindingSet[] bindingSets) + throws Exception { + return new MultiSearchIterator(bindingSets); + } + + /** + * Get HTTP request for this particular query & binding. + * @param binding + * @return + */ + private Request getHttpRequest(IBindingSet binding) { + // FIXME: real endpoint URL + Request request = client.newRequest("https://en.wikipedia.org/w/api.php"); + request.method(HttpMethod.GET); + // Using XML for now to use XPath on responses + request.param("format", "xml"); + // Add fixed params + for (Map.Entry<String, String> param : template.getFixedParams().entrySet()) { + request.param(param.getKey(), param.getValue()); + } + // Resolve variable params + for (Map.Entry<String, IVariableOrConstant> term : inputVars.entrySet()) { + String value = null; + IV boundValue = null; + if (term != null) { + boundValue = (IV)term.getValue().get(binding); + } + if (boundValue == null) { + // try default + value = template.getInputDefault(term.getKey()); + } else { + value = boundValue.stringValue(); + } + if (value == null) { + throw new IllegalArgumentException("Could not find binding for parameter " + term.getKey()); + } + request.param(term.getKey(), value); + } + + return request; + } + + @Override + public List<IVariable<IV>> getMockVariables() { + List<IVariable<IV>> externalVars = new LinkedList<IVariable<IV>>(); + for (OutputVariable v: outputVars) { + externalVars.add(v.getVar()); + } + return externalVars; + } + + /** + * A chunk of calls to resolve labels. + */ + private class MultiSearchIterator implements ICloseableIterator<IBindingSet> { + /** + * Binding sets being resolved in this chunk. + */ + private final IBindingSet[] bindingSets; + /** + * Has this chunk been closed? + */ + private boolean closed; + /** + * Index of the next binding set to handle when next is next called. + */ + private int i; + /** + * Current search result. + */ + private SearchResultsIterator searchResult; + + MultiSearchIterator(IBindingSet[] bindingSets) { + this.bindingSets = bindingSets; + searchResult = doNextSearch(); + } + + @Override + public boolean hasNext() { + if (closed || i >= bindingSets.length) { + return false; + } + + if (searchResult == null) { + return false; + } + + if (searchResult.hasNext()) { + return true; + } + + searchResult = doNextSearch(); + if (searchResult == null) { + return false; + } else { + return searchResult.hasNext(); + } + } + + /** + * Produce next search results iterator. Skips over empty results. + * @return Result iterator or null if no more results. + */ + private SearchResultsIterator doNextSearch() { + // Just in case, double check + if (closed || bindingSets == null || i >= bindingSets.length) { + searchResult = null; + return null; + } + SearchResultsIterator result; + do { + IBindingSet binding = bindingSets[i++]; + result = doSearchFromBinding(binding); + } while (!result.hasNext() && i < bindingSets.length); + if (result.hasNext()) { + return result; + } else { + return null; + } + } + + /** + * Execute search for one specific binding set. + * @param binding + * @return Search results iterator. + */ + private SearchResultsIterator doSearchFromBinding(IBindingSet binding) { + final Request req = getHttpRequest(binding); + log.info("REQUEST: " + req.getQuery()); + for (OutputVariable var: outputVars) { + binding.set(var.getVar(), new Constant(mock("TEST"))); + } + return new SearchResultsIterator(new IBindingSet[] {binding}); + } + + /** + * Build a mock IV from a literal string. + */ + private IV mock(String literalString) { + Literal literal = new LiteralImpl(literalString); + TermId mock = TermId.mockIV(VTE.LITERAL); + mock.setValue(lexiconRelation.getValueFactory().asValue(literal)); + return mock; + } + + @Override + public IBindingSet next() { + if (closed || i >= bindingSets.length || searchResult == null) { + return null; + } + + if (searchResult.hasNext()) { + return searchResult.next(); + } + + searchResult = doNextSearch(); + if (searchResult == null || !searchResult.hasNext()) { + return null; + } else { + return searchResult.next(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + closed = true; + } + } + + /** + * Simple iterator for search results. + */ + private class SearchResultsIterator implements Iterator<IBindingSet> { + /** + * The index into the array of hits wrapped by this iterator. + */ + private int i; + /** + * The array of hits wrapped by this iterator. + */ + private final IBindingSet[] results; + + SearchResultsIterator(final IBindingSet[] results) { + if (results == null) { + throw new IllegalArgumentException("Null result?"); + } + + this.results = results; + } + + @Override + public boolean hasNext() { + return i < results.length; + } + + @Override + public IBindingSet next() { + return results[i++]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return "SearchResultsIterator{nhits=" + results.length + "} : " + results; + } + } +} diff --git a/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceFactory.java b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceFactory.java new file mode 100644 index 0000000..f22b3a4 --- /dev/null +++ b/blazegraph/src/main/java/org/wikidata/query/rdf/blazegraph/mwapi/MWApiServiceFactory.java @@ -0,0 +1,153 @@ +package org.wikidata.query.rdf.blazegraph.mwapi; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.openrdf.model.URI; +import org.openrdf.model.impl.URIImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.wikidata.query.rdf.common.uri.Ontology; + +import com.bigdata.rdf.sparql.ast.eval.AbstractServiceFactory; +import com.bigdata.rdf.sparql.ast.eval.ServiceParams; +import com.bigdata.rdf.sparql.ast.service.BigdataNativeServiceOptions; +import com.bigdata.rdf.sparql.ast.service.BigdataServiceCall; +import com.bigdata.rdf.sparql.ast.service.IServiceOptions; +import com.bigdata.rdf.sparql.ast.service.ServiceCallCreateParams; +import com.bigdata.rdf.sparql.ast.service.ServiceNode; +import com.bigdata.rdf.sparql.ast.service.ServiceRegistry; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Service factory for calling out to Mediawiki API Services. + * Service call looks like: + * + * SERVICE wikibase:mwapi { + * bd:serviceParam wikibase:api "Categories" . + * bd:serviceParam :titles "Albert Einstein" . + * bd:serviceParam :category ?category . + * bd:serviceParam :title ?title . + * } + */ +public class MWApiServiceFactory extends AbstractServiceFactory { + private static final Logger log = LoggerFactory.getLogger(MWApiServiceFactory.class); + + /** + * Options configuring this service as a native Blazegraph service. + */ + public static final BigdataNativeServiceOptions SERVICE_OPTIONS = new BigdataNativeServiceOptions(); + /** + * Service config. + */ + private final Map<String, ApiTemplate> config = new HashMap<>(); + /** + * The URI service key. + */ + public static final URI SERVICE_KEY = new URIImpl(Ontology.NAMESPACE + "mwapi"); + /** + * API type parameter name. + */ + public static final URI API_KEY = new URIImpl(Ontology.NAMESPACE + "api"); + /** + * Default service config filename. + */ + public static final String CONFIG_DEFAULT = "mwservices.json"; + /** + * Config file parameter. + */ + public static final String CONFIG_NAME = MWApiServiceFactory.class.getName() + ".config"; + /** + * Filename of the config. + */ + public static final String CONFIG_FILE = System.getProperty(CONFIG_NAME, CONFIG_DEFAULT); + + public MWApiServiceFactory() throws IOException { + log.info("Loading MWAPI service configuration from " + CONFIG_FILE); + loadJSONConfig(new InputStreamReader(new FileInputStream(CONFIG_FILE), StandardCharsets.UTF_8)); + log.info("Registered " + config.size() + " services."); + } + + /** + * @throws IOException + * Load config from JSON object. + * @param configReader + */ + public void loadJSONConfig(Reader configReader) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode node = mapper.readTree(IOUtils.toString(configReader)); + for (String serviceName: (Iterable<String>)() -> node.fieldNames()) { + config.put(serviceName, ApiTemplate.fromJSON(node.get(serviceName))); + } + } + + @Override + public IServiceOptions getServiceOptions() { + return SERVICE_OPTIONS; + } + + /** + * Register the service so it is recognized by Blazegraph. + */ + public static void register() { + final ServiceRegistry reg = ServiceRegistry.getInstance(); + try { + reg.add(SERVICE_KEY, new MWApiServiceFactory()); + } catch (IOException e) { + // Do not add to whitelist if init failed. + log.warn("MW Service registration failed: " + e); + return; + } + reg.addWhitelistURL(SERVICE_KEY.toString()); + } + + @Override + public BigdataServiceCall create(ServiceCallCreateParams params, final ServiceParams serviceParams) { + final ServiceNode serviceNode = params.getServiceNode(); + + if (serviceNode == null) { + throw new IllegalArgumentException(); + } + + final ApiTemplate template = getServiceTemplate(serviceParams); + + return new MWApiServiceCall(template, + template.getInputVars(serviceParams), + template.getOutputVars(serviceParams), + params.getClientConnectionManager(), + params.getTripleStore().getLexiconRelation() + ); + } + + /** + * Extract service template name from params. + * @param serviceParams + * @return Service template + */ + private ApiTemplate getServiceTemplate(final ServiceParams serviceParams) { + final String templateName = serviceParams.getAsString(API_KEY); + serviceParams.clear(API_KEY); + if (!config.containsKey(templateName)) { + throw new IllegalArgumentException( + "Service name " + templateName + " not found in configuration"); + } + return config.get(templateName); + } + + /** + * Create predicate parameter URI from name. + * TODO: for now, identical to name, check if that works. + * @param name + * @return + */ + public static URI paramNameToURI(String name) { + return new URIImpl(name); + } +} diff --git a/dist/src/script/runBlazegraph.sh b/dist/src/script/runBlazegraph.sh index 3027837..6cd7419 100755 --- a/dist/src/script/runBlazegraph.sh +++ b/dist/src/script/runBlazegraph.sh @@ -62,6 +62,7 @@ -Dorg.wikidata.query.rdf.blazegraph.inline.literal.WKTSerializer.noGlobe=$DEFAULT_GLOBE \ -Dcom.bigdata.rdf.sail.webapp.client.RemoteRepository.maxRequestURLLength=7168 \ -Dcom.bigdata.rdf.sail.sparql.PrefixDeclProcessor.additionalDeclsFile=$DIR/prefixes.conf \ + -Dorg.wikidata.query.rdf.blazegraph.mwapi.MWApiServiceFactory.config=$DIR/mwservices.json \ -Dcom.bigdata.rdf.sail.webapp.client.HttpClientConfigurator=org.wikidata.query.rdf.blazegraph.ProxiedHttpConnectionFactory \ -Dhttp.userAgent="${USER_AGENT}" \ ${BLAZEGRAPH_OPTS} \ diff --git a/pom.xml b/pom.xml index ad20dae..657f964 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,7 @@ <properties> <blazegraph.version>2.1.5-SNAPSHOT</blazegraph.version> + <jackson.version>2.2.3</jackson.version> <jetty.version>9.2.3.v20140905</jetty.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <sesame.version>2.7.12</sesame.version> @@ -99,6 +100,21 @@ <type>pom</type> </dependency> <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> diff --git a/tools/pom.xml b/tools/pom.xml index a55ea6d..b13d3aa 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -263,24 +263,28 @@ <value>org.wikidata.query.rdf.blazegraph.WikibaseOptimizers</value> </property> <property> - <name>org.wikidata.query.rdf.blazegraph.inline.literal.WKTSerializer.noGlobe</name> - <value>2</value> + <name>org.wikidata.query.rdf.blazegraph.inline.literal.WKTSerializer.noGlobe</name> + <value>2</value> </property> <property> - <name>com.bigdata.rdf.sail.sparql.PrefixDeclProcessor.additionalDeclsFile</name> - <value>${project.parent.basedir}/dist/src/script/prefixes.conf</value> + <name>com.bigdata.rdf.sail.sparql.PrefixDeclProcessor.additionalDeclsFile</name> + <value>${project.parent.basedir}/dist/src/script/prefixes.conf</value> </property> <property> - <name>com.bigdata.rdf.sail.webapp.client.HttpClientConfigurator</name> - <value>org.wikidata.query.rdf.blazegraph.ProxiedHttpConnectionFactory</value> + <name>com.bigdata.rdf.sail.webapp.client.HttpClientConfigurator</name> + <value>org.wikidata.query.rdf.blazegraph.ProxiedHttpConnectionFactory</value> </property> <property> - <name>http.userAgent</name> - <value>Wikidata Query Service (testing); https://query.wikidata.org/</value> + <name>http.userAgent</name> + <value>Wikidata Query Service (testing); https://query.wikidata.org/</value> </property> <property> - <name>wikibaseServiceWhitelist</name> - <value>${project.basedir}/src/test/resources/blazegraph/whitelist.txt</value> + <name>wikibaseServiceWhitelist</name> + <value>${project.basedir}/src/test/resources/blazegraph/whitelist.txt</value> + </property> + <property> + <name>org.wikidata.query.rdf.blazegraph.mwapi.MWApiServiceFactory.config</name> + <value>${project.basedir}/src/test/resources/blazegraph/services.json</value> </property> </properties> </configuration> diff --git a/tools/runBlazegraph.sh b/tools/runBlazegraph.sh index d02e41d..b9e9346 100755 --- a/tools/runBlazegraph.sh +++ b/tools/runBlazegraph.sh @@ -1,3 +1,4 @@ #!/bin/bash +export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n" mvn -PrunBlazegraph generate-sources diff --git a/tools/src/test/resources/blazegraph/services.json b/tools/src/test/resources/blazegraph/services.json new file mode 100644 index 0000000..b4a93e5 --- /dev/null +++ b/tools/src/test/resources/blazegraph/services.json @@ -0,0 +1,22 @@ +{ + "Categories": { + "params": { + "action": "query", + "prop": "categories", + "titles": { + "type": "list" + }, + "cllimit": { + "type": "int", + "default": 500 + } + }, + "output": { + "items": "//api/query/pages/page/categories", + "vars": { + "category": "@title", + "title": "//api/query/pages/page/@title" + } + } + } +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/349119 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: If0aa5c213e197f6b20b55092680930eb82d1a0a2 Gerrit-PatchSet: 1 Gerrit-Project: wikidata/query/rdf Gerrit-Branch: master Gerrit-Owner: Smalyshev <smalys...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits