Commit f4d4ed4b authored by Florian Schäfer's avatar Florian Schäfer

Allow ApiQueries to convert the deserialization result to a different object

The query that gets the labels for a wikidata item is the first one that does this, it deserializes the JSON to `WbgetentitiesResult`, but then returns a `Map<String,String>` containing only the labels.
parent 6ac45e9a
package org.wikipedia.api;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.function.Function;
import org.openstreetmap.josm.tools.HttpClient;
import org.openstreetmap.josm.tools.Utils;
import org.wikipedia.WikipediaPlugin;
public abstract class ApiQuery<T> {
private final SerializationSchema<T> schema;
private final URL url;
private final long cacheExpiryTime;
private final IOExceptionFunction<InputStream, T> deserializeFunc;
protected ApiQuery(final URL url, final SerializationSchema<T> schema, final long cacheExpiryTime) {
this.schema = schema;
protected <S> ApiQuery(final URL url, final SerializationSchema<S> schema, final long cacheExpiryTime, final Function<S, T> resultConverter) {
this.url = url;
this.deserializeFunc = stream -> resultConverter.apply(schema.getMapper().readValue(stream, schema.getSchemaClass()));
if (cacheExpiryTime >= 1) {
this.cacheExpiryTime = cacheExpiryTime;
} else {
......@@ -20,6 +23,14 @@ public abstract class ApiQuery<T> {
}
}
protected ApiQuery(final URL url, final SerializationSchema<T> schema, final long cacheExpiryTime) {
this(url, schema, cacheExpiryTime, it -> it);
}
protected ApiQuery(final URL url, final SerializationSchema<T> schema) {
this(url, schema, -1);
}
/**
* @return the number of milliseconds for which the API response should be retrieved
* from the cache without trying to get it directly from the API
......@@ -41,11 +52,8 @@ public abstract class ApiQuery<T> {
public abstract HttpClient getHttpClient();
/**
* @return the schema that is used to get from the JSON encoded response to its Java representation
*/
public final SerializationSchema<T> getSchema() {
return schema;
public final T deserialize(final InputStream stream) throws IOException {
return deserializeFunc.apply(stream);
}
/**
......@@ -67,4 +75,8 @@ public abstract class ApiQuery<T> {
return result + Utils.encodeUrl(" " + String.join(" ", keywords));
}
@FunctionalInterface
private interface IOExceptionFunction<T, R> {
R apply(T t) throws IOException;
}
}
......@@ -45,10 +45,7 @@ public final class ApiQueryClient {
final String remoteResponse = new String(IOUtils.toByteArray(getInputStreamForQuery(query)), StandardCharsets.UTF_8);
Caches.API_RESPONSES.put(query.getCacheKey(), remoteResponse);
Logging.info("Successfully updated API cache for " + query.getCacheKey());
return query.getSchema().getMapper().readValue(
new ByteArrayInputStream(remoteResponse.getBytes(StandardCharsets.UTF_8)),
query.getSchema().getSchemaClass()
);
return query.deserialize(new ByteArrayInputStream(remoteResponse.getBytes(StandardCharsets.UTF_8)));
} catch (IOException e) {
if (cachedValue == null) {
throw wrapReadDecodeJsonExceptions(e, query.getApiName());
......@@ -64,7 +61,7 @@ public final class ApiQueryClient {
}
try {
return query.getSchema().getMapper().readValue(stream, query.getSchema().getSchemaClass());
return query.deserialize(stream);
} catch (IOException e) {
throw wrapReadDecodeJsonExceptions(e, query.getApiName());
}
......
......@@ -19,7 +19,7 @@ public class WdqApiQuery<T> extends ApiQuery<T> {
private final String queryString;
public WdqApiQuery(final URL url, final String queryString, final SerializationSchema<T> schema) {
super(url, schema, -1);
super(url, schema);
this.queryString = Objects.requireNonNull(queryString);
}
......
......@@ -3,6 +3,9 @@ package org.wikipedia.api.wikidata_action;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.openstreetmap.josm.tools.HttpClient;
import org.openstreetmap.josm.tools.Utils;
import org.wikipedia.api.ApiQuery;
......@@ -17,19 +20,24 @@ public final class WikidataActionApiQuery<T> extends ApiQuery<T> {
private static final String FORMAT_PARAMS = "format=json&utf8=1&formatversion=1";
private static final String[] TICKET_KEYWORDS = {"wikidata", "ActionAPI"};
private final String query;
private final String queryString;
private WikidataActionApiQuery(final String query, final SerializationSchema<T> schema, final long cacheExpiryTime) {
super(defaultUrl, schema, cacheExpiryTime);
this.query = query;
this.queryString = query;
}
private WikidataActionApiQuery(final String query, final SerializationSchema<T> schema) {
this(query, schema, -1);
}
public String getQuery() {
return query;
private <S> WikidataActionApiQuery(final String queryString, final SerializationSchema<S> schema, final Function<S, T> converter) {
super(defaultUrl, schema, -1, converter);
this.queryString = queryString;
}
public String getQueryString() {
return queryString;
}
public static WikidataActionApiQuery<SitematrixResult> sitematrix() {
......@@ -68,16 +76,20 @@ public final class WikidataActionApiQuery<T> extends ApiQuery<T> {
);
}
public static WikidataActionApiQuery<WbgetentitiesResult> wbgetentitiesLabels(final String qId) {
public static WikidataActionApiQuery<Map<String, String>> wbgetentitiesLabels(final String qId) {
if (!RegexUtil.isValidQId(qId)) {
throw new IllegalArgumentException("Invalid Q-ID: " + qId);
}
return new WikidataActionApiQuery<>(FORMAT_PARAMS + "&action=wbgetentities&props=labels&ids=" + qId, WbgetentitiesResult.SCHEMA);
return new WikidataActionApiQuery<>(
FORMAT_PARAMS + "&action=wbgetentities&props=labels&ids=" + qId,
WbgetentitiesResult.SCHEMA,
result -> result.getEntities().values().stream().findFirst().map(WbgetentitiesResult.Entity::getLabels).orElse(new HashMap<>())
);
}
@Override
public String getCacheKey() {
return getUrl().toString() + '?' + getQuery();
return getUrl().toString() + '?' + getQueryString();
}
@Override
......@@ -91,7 +103,7 @@ public final class WikidataActionApiQuery<T> extends ApiQuery<T> {
.setAccept("application/json")
.setHeader("Content-Type", "text/plain; charset=utf-8")
.setHeader("User-Agent", getUserAgent(TICKET_KEYWORDS))
.setReasonForRequest(getQuery().replace('&', ' '))
.setRequestBody(getQuery().getBytes(StandardCharsets.UTF_8));
.setReasonForRequest(getQueryString().replace('&', ' '))
.setRequestBody(getQueryString().getBytes(StandardCharsets.UTF_8));
}
}
......@@ -19,7 +19,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.wikipedia.api.SerializationSchema;
public final class WbgetentitiesResult {
......@@ -138,7 +137,7 @@ public final class WbgetentitiesResult {
private final String id;
private final String type;
private final Map<String, Sitelink> sitelinks = new HashMap<>();
private final Set<Label> labels = new HashSet<>();
private final Map<String, String> labels = new HashMap<>();
@JsonCreator
public Entity(
......@@ -153,7 +152,7 @@ public final class WbgetentitiesResult {
this.sitelinks.putAll(sitelinks);
}
if (labels != null) {
this.labels.addAll(labels.values());
labels.values().forEach(label -> this.labels.put(label.getLanguage(), label.getValue()));
}
}
......@@ -165,8 +164,8 @@ public final class WbgetentitiesResult {
return type;
}
public Collection<Label> getLabels() {
return labels;
public Map<String, String> getLabels() {
return Collections.unmodifiableMap(labels);
}
public Collection<Sitelink> getSitelinks() {
......
......@@ -20,7 +20,7 @@ public class WikipediaActionApiQuery<T> extends ApiQuery<T> {
private final String queryString;
private WikipediaActionApiQuery(final IWikipediaSite site, final String queryString, SerializationSchema<T> schema) {
super(ApiUrl.url(site.getSite().getUrl(), "/w/api.php"), schema, -1);
super(ApiUrl.url(site.getSite().getUrl(), "/w/api.php"), schema);
this.queryString = Objects.requireNonNull(queryString);
}
......
......@@ -74,11 +74,11 @@ public class WikidataActionApiQueryTest {
public void testWbgetentitiesQuery() {
assertEquals(
"format=json&utf8=1&formatversion=1&action=wbgetentities&sites=&props=&ids=Q1",
WikidataActionApiQuery.wbgetentities(Collections.singletonList("Q1")).getQuery()
WikidataActionApiQuery.wbgetentities(Collections.singletonList("Q1")).getQueryString()
);
assertEquals(
"format=json&utf8=1&formatversion=1&action=wbgetentities&sites=&props=&ids=Q1%7CQ13%7CQ24%7CQ20150617%7CQ42%7CQ12345",
WikidataActionApiQuery.wbgetentities(Arrays.asList("Q1", "Q13", "Q24", "Q20150617", "Q42", "Q12345")).getQuery()
WikidataActionApiQuery.wbgetentities(Arrays.asList("Q1", "Q13", "Q24", "Q20150617", "Q42", "Q12345")).getQueryString()
);
}
......@@ -163,17 +163,13 @@ public class WikidataActionApiQueryTest {
)
);
final WbgetentitiesResult result = ApiQueryClient.query(WikidataActionApiQuery.wbgetentitiesLabels("Q42"));
assertEquals(1, result.getEntities().size());
assertEquals(138, result.getEntities().entrySet().iterator().next().getValue().getLabels().size());
assertEquals(0, result.getEntities().entrySet().iterator().next().getValue().getSitelinks().size());
assertEquals("Q42", result.getEntities().entrySet().iterator().next().getValue().getId());
assertEquals("item", result.getEntities().entrySet().iterator().next().getValue().getType());
assertEquals("Douglas Adams", result.getEntities().entrySet().iterator().next().getValue().getLabels().stream().filter(it -> "en".equals(it.getLanguage())).findAny().get().getValue());
assertEquals("Дуглас Адамс", result.getEntities().entrySet().iterator().next().getValue().getLabels().stream().filter(it -> "ru".equals(it.getLanguage())).findAny().get().getValue());
assertEquals("더글러스 애덤스", result.getEntities().entrySet().iterator().next().getValue().getLabels().stream().filter(it -> "ko".equals(it.getLanguage())).findAny().get().getValue());
assertEquals("ಡಾಗ್ಲಸ್ ಆಡಮ್ಸ್", result.getEntities().entrySet().iterator().next().getValue().getLabels().stream().filter(it -> "tcy".equals(it.getLanguage())).findAny().get().getValue());
final Map<String, String> result = ApiQueryClient.query(WikidataActionApiQuery.wbgetentitiesLabels("Q42"));
assertEquals(138, result.size());
assertEquals("Douglas Adams", result.get("en"));
assertEquals("Дуглас Адамс", result.get("ru"));
assertEquals("더글러스 애덤스", result.get("ko"));
assertEquals("ಡಾಗ್ಲಸ್ ಆಡಮ್ಸ್", result.get("tcy"));
verify(postRequestedFor(urlEqualTo("/")).withRequestBody(new EqualToPattern("format=json&utf8=1&formatversion=1&action=wbgetentities&props=labels&ids=Q42")));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment