diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
deleted file mode 100644
index 3abb8f2..0000000
--- a/.mvn/extensions.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- io.takari.polyglot
- polyglot-groovy
- 0.4.3
-
-
-
diff --git a/README.md b/README.md
index 3a0d557..7c1176b 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ An 100% compliant [ipdata.co](https://ipdata.co) API java client.
- [Install](#install)
- [Use](#use)
- [Configuration](#configuration)
- - [API](#create-an-instance)
+ - [API](#api)
- [Basic Usage](#basic-usage)
- [Connection reuse and HTTP/2](#connection-reuse-and-http2)
- [Single Field Selection](#single-field-selection)
@@ -81,30 +81,38 @@ Output:
{
"ip": "1.1.1.1",
"is_eu": false,
- "city": null,
- "region": null,
- "region_code": null,
- "region_type": null,
- "country_name": "Australia",
- "country_code": "AU",
- "continent_name": "Oceania",
- "continent_code": "OC",
- "latitude": -33.494,
- "longitude": 143.2104,
- "postal": null,
- "calling_code": "61",
- "flag": "https://ipdata.co/flags/au.png",
- "emoji_flag": "\ud83c\udde6\ud83c\uddfa",
- "emoji_unicode": "U+1F1E6 U+1F1FA",
+ "is_satellite": false,
+ "is_anycast": false,
+ "city": "Los Angeles",
+ "organisation": null,
+ "region": "California",
+ "region_code": "CA",
+ "region_type": "state",
+ "country_name": "United States",
+ "country_code": "US",
+ "continent_name": "North America",
+ "continent_code": "NA",
+ "latitude": 34.0522,
+ "longitude": -118.2437,
+ "postal": "90001",
+ "calling_code": "1",
+ "flag": "https://ipdata.co/flags/us.png",
+ "emoji_flag": "\ud83c\uddfa\ud83c\uddf8",
+ "emoji_unicode": "U+1F1FA U+1F1F8",
"asn": {
"asn": "AS13335",
- "name": "Cloudflare, Inc.",
+ "name": "Cloudflare Inc",
"domain": "cloudflare.com",
"route": "1.1.1.0/24",
"type": "hosting"
},
+ "carrier": {
+ "name": "Cloudflare",
+ "mcc": "310",
+ "mnc": "000"
+ },
"company": {
- "name": "Cloudflare, Inc.",
+ "name": "Cloudflare Inc",
"domain": "cloudflare.com",
"network": "1.1.1.0/24",
"type": "hosting"
@@ -112,25 +120,28 @@ Output:
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en",
+ "rtl": 0
}
],
"currency": {
- "name": "Australian Dollar",
- "code": "AUD",
- "symbol": "AU$",
+ "name": "US Dollar",
+ "code": "USD",
+ "symbol": "$",
"native": "$",
- "plural": "Australian dollars"
+ "plural": "US dollars"
},
"time_zone": {
- "name": "Australia/Sydney",
- "abbr": "AEDT",
- "offset": "+1100",
+ "name": "America/Los_Angeles",
+ "abbr": "PDT",
+ "offset": "-0700",
"is_dst": true,
- "current_time": "2020-01-29T20:22:52.283874+11:00"
+ "current_time": "2020-06-12T06:37:16.595612-07:00"
},
"threat": {
"is_tor": false,
+ "is_vpn": false,
"is_proxy": false,
"is_anonymous": false,
"is_known_attacker": false,
@@ -138,9 +149,16 @@ Output:
"is_threat": false,
"is_bogon": false,
"is_icloud_relay": false,
- "is_datacenter": false
+ "is_datacenter": false,
+ "blocklists": [],
+ "scores": {
+ "vpn_score": 0,
+ "proxy_score": 0,
+ "threat_score": 0,
+ "trust_score": 100
+ }
},
- "count": "0"
+ "count": "1500"
}
```
@@ -151,6 +169,7 @@ The following fields were added to the API responses recently and are mapped by
- `threat.blocklists[].first_seen` – `Blocklist.getFirstSeen()`: the first day the address was seen on that blocklist, as a `YYYY-MM-DD` (UTC) string, or `null` when the feed does not report it.
- `threat.blocklists[].last_seen` – `Blocklist.getLastSeen()`: the most recent such day, same format, or `null`.
- `threat.blocklists[].days_seen` – `Blocklist.getDaysSeen()`: the number of days the address was seen on that blocklist, as an `Integer`, or `null`.
+- `languages[].code` – `Language.code()`: the code of the language (`en` for English), or `null` when the API does not send it.
#### Connection reuse and HTTP/2
An `IpdataService` keeps a persistent HTTPS connection pool for as long as it lives. Create one service per process (or one per API key or endpoint), keep it, and reuse it for every lookup: each new service pays a fresh TLS handshake. Requests to api.ipdata.co are multiplexed over HTTP/2 when the platform negotiates it, and fall back to HTTP/1.1 keep-alive otherwise.
@@ -206,7 +225,7 @@ IpdataModel flags = ipdataService.getFields("1.1.1.1", IpdataField.IS_SATELLITE,
```
-### Bulk data
+#### Bulk data
You can as well get multiple responses at once by using the ``bulk`` api:
```java
diff --git a/pom.xml b/pom.xml
index b20011d..b242050 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,9 @@
0.8.14
3.5.5
11.10
+ 2.22.4
+
+ 2.22
@@ -57,6 +60,16 @@
feign-java11
${version.client.feign}
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${version.client.jackson}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${version.client.jackson.annotations}
+
io.github.openfeign
feign-httpclient
@@ -66,7 +79,7 @@
com.google.guava
guava
- 33.4.8-jre
+ 33.7.2-jre
org.slf4j
diff --git a/src/main/java/io/ipdata/client/model/Language.java b/src/main/java/io/ipdata/client/model/Language.java
index 395358c..a300465 100644
--- a/src/main/java/io/ipdata/client/model/Language.java
+++ b/src/main/java/io/ipdata/client/model/Language.java
@@ -10,5 +10,7 @@ public class Language {
private String name;
@JsonProperty("native")
private String nativeName;
+ /** The language code, for example {@code en} for English; {@code null} when the API does not send it. */
+ private String code;
private int rtl;
}
diff --git a/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java b/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java
index e526eb7..c2fcea1 100644
--- a/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java
+++ b/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java
@@ -13,6 +13,7 @@
import java.io.IOException;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
@RequiredArgsConstructor
@Slf4j
@@ -30,7 +31,7 @@ public Exception decode(String methodKey, Response response) {
URL url = new URL(response.request().url());
logger.error("An error occurred during an Ipdata API call: got status '{}' for path '{}'",
responseCode, url.getPath());
- message = CharStreams.toString(response.body().asReader());
+ message = CharStreams.toString(response.body().asReader(StandardCharsets.UTF_8));
Error error = mapper.readValue(message, Error.class);
if (responseCode == RATE_LIMIT_STATUS) {
return new RateLimitException(error.getMessage(), responseCode);
diff --git a/src/main/java/io/ipdata/client/service/FieldDecoder.java b/src/main/java/io/ipdata/client/service/FieldDecoder.java
index 1eb1b58..fb0bd38 100644
--- a/src/main/java/io/ipdata/client/service/FieldDecoder.java
+++ b/src/main/java/io/ipdata/client/service/FieldDecoder.java
@@ -8,6 +8,7 @@
import java.io.IOException;
import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
/**
* The API returns usable but invalid(/unquoted) strings for String fields
@@ -22,7 +23,7 @@ class FieldDecoder implements Decoder {
@Override
public Object decode(Response response, Type type) throws IOException {
if (type.equals(String.class)) {
- return CharStreams.toString(response.body().asReader());
+ return CharStreams.toString(response.body().asReader(StandardCharsets.UTF_8));
}
return mapper.readValue(response.body().asInputStream(), mapper.constructType(type));
}
diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java
index b22ef6d..22bcc40 100644
--- a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java
+++ b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java
@@ -19,6 +19,7 @@
import org.slf4j.LoggerFactory;
import java.net.URL;
+import java.time.Duration;
import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE;
@@ -64,7 +65,7 @@ public static IpdataServiceBuilder of(URL url, String key, CacheConfig cacheConf
public IpdataService build() {
final ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(SNAKE_CASE);
- mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
@@ -98,13 +99,14 @@ public IpdataService build() {
.target(IpdataInternalSingleFieldClient.class, url.toString());
if (cacheConfig != null) {
+ final Duration expiry = Duration.of(cacheConfig.timeout(), cacheConfig.unit().toChronoUnit());
CachingInternalClient cachingInternalClient = CachingInternalClient.builder()
.expiry(cacheConfig.timeout())
.unit(cacheConfig.unit())
.maxSize(cacheConfig.maxSize())
.ipdataCache(
CacheBuilder.newBuilder()
- .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit())
+ .expireAfterWrite(expiry)
.maximumSize(cacheConfig.maxSize())
.build(new CacheLoader() {
@Override
@@ -115,7 +117,7 @@ public IpdataModel load(String key) throws Exception {
)
.fieldsCache(
CacheBuilder.newBuilder()
- .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit())
+ .expireAfterWrite(expiry)
.maximumSize(cacheConfig.maxSize())
.build(new CacheLoader, IpdataModel>() {
@Override
@@ -126,7 +128,7 @@ public IpdataModel load(HashPair key) throws Exception {
)
.asnCache(
CacheBuilder.newBuilder()
- .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit())
+ .expireAfterWrite(expiry)
.maximumSize(cacheConfig.maxSize())
.build(new CacheLoader() {
@Override
@@ -137,7 +139,7 @@ public AsnModel load(String key) throws Exception {
)
.tzCache(
CacheBuilder.newBuilder()
- .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit())
+ .expireAfterWrite(expiry)
.maximumSize(cacheConfig.maxSize())
.build(new CacheLoader() {
@Override
@@ -148,7 +150,7 @@ public TimeZone load(String key) throws Exception {
)
.currencyCache(
CacheBuilder.newBuilder()
- .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit())
+ .expireAfterWrite(expiry)
.maximumSize(cacheConfig.maxSize())
.build(new CacheLoader() {
@Override
@@ -159,7 +161,7 @@ public Currency load(String key) throws Exception {
)
.threatCache(
CacheBuilder.newBuilder()
- .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit())
+ .expireAfterWrite(expiry)
.maximumSize(cacheConfig.maxSize())
.build(new CacheLoader() {
@Override
diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java
index 7531ba8..78982b5 100644
--- a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java
+++ b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java
@@ -21,12 +21,12 @@ class IpdataServiceSupport implements IpdataService {
private final IpdataInternalSingleFieldClient singleFieldClient;
@Delegate(types = IpdataInternalClient.class)
- public IpdataInternalClient getSingleFieldClient() {
+ private IpdataInternalClient getApi() {
return api;
}
@Delegate(types = IpdataInternalSingleFieldClient.class)
- private IpdataInternalSingleFieldClient getApi() {
+ private IpdataInternalSingleFieldClient getSingleFieldClient() {
return singleFieldClient;
}
diff --git a/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java b/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java
index f8f6f38..917ee4b 100644
--- a/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java
+++ b/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java
@@ -36,7 +36,7 @@ public Response execute(Request request, Request.Options options) throws IOExcep
String fixedUrl = fixedPath + query;
request = Request.create(
request.httpMethod(), fixedUrl, request.headers(),
- request.body(), request.charset()
+ request.body(), request.charset(), request.requestTemplate()
);
}
return delegate.execute(request, options);
diff --git a/src/test/java/io/ipdata/client/LanguageTest.java b/src/test/java/io/ipdata/client/LanguageTest.java
new file mode 100644
index 0000000..39422ac
--- /dev/null
+++ b/src/test/java/io/ipdata/client/LanguageTest.java
@@ -0,0 +1,80 @@
+package io.ipdata.client;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.ipdata.client.model.IpdataModel;
+import io.ipdata.client.model.Language;
+import io.ipdata.client.service.IpdataService;
+import lombok.SneakyThrows;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static io.ipdata.client.service.IpdataField.LANGUAGES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * The {@code code} attribute of the {@code languages[]} entries.
+ */
+public class LanguageTest {
+
+ private static final MockIpdataServer MOCK = MockIpdataServer.getInstance();
+ private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MOCK.getUrl());
+
+ private static List services() {
+ return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService());
+ }
+
+ private static Language onlyLanguage(IpdataModel model) {
+ assertEquals(1, model.languages().size());
+ return model.languages().get(0);
+ }
+
+ @Test
+ @SneakyThrows
+ public void codeIsMappedOnTheFullModel() {
+ for (IpdataService service : services()) {
+ Language english = onlyLanguage(service.ipdata("8.8.8.8"));
+ assertEquals("English", english.name());
+ assertEquals("English", english.nativeName());
+ assertEquals("en", english.code());
+ assertEquals("pt", onlyLanguage(service.ipdata("187.13.250.1")).code());
+ assertEquals("ar", onlyLanguage(service.ipdata("41.128.21.123")).code());
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void codeIsMappedOnFieldSelection() {
+ for (IpdataService service : services()) {
+ IpdataModel model = service.getFields("8.8.8.8", LANGUAGES);
+ assertEquals("en", onlyLanguage(model).code());
+ assertNull(model.city());
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void codeIsNullWhenAbsent() {
+ String ip = "203.0.113.8";
+ ObjectNode response = (ObjectNode) TEST_CONTEXT.mapper().readTree(TEST_CONTEXT.get("/8.8.8.8", null));
+ response.put("ip", ip);
+ ((ObjectNode) response.get("languages").get(0)).remove("code");
+ MOCK.registerFixture(ip, response.toString());
+
+ for (IpdataService service : services()) {
+ Language language = onlyLanguage(service.ipdata(ip));
+ assertEquals("English", language.name());
+ assertNull(language.code());
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void strictMapperAcceptsTheCodeAttribute() {
+ // the test mapper fails on unknown properties, so every attribute the fixtures carry must exist on the model
+ IpdataModel model = TEST_CONTEXT.mapper().readValue(TEST_CONTEXT.get("/8.8.8.8", null), IpdataModel.class);
+ assertEquals("en", onlyLanguage(model).code());
+ }
+}
diff --git a/src/test/java/io/ipdata/client/MappingTest.java b/src/test/java/io/ipdata/client/MappingTest.java
index 0d5c5fd..77eb722 100644
--- a/src/test/java/io/ipdata/client/MappingTest.java
+++ b/src/test/java/io/ipdata/client/MappingTest.java
@@ -8,6 +8,7 @@
import org.junit.Test;
import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
public class MappingTest {
@@ -16,7 +17,7 @@ public class MappingTest {
@Test @SneakyThrows
public void testMapping(){
String actual = TEST_CONTEXT.mapper().writeValueAsString(TEST_CONTEXT.mapper().readValue(getClass().getResourceAsStream("fixture.json"), IpdataModel.class));
- String expected = CharStreams.toString(new InputStreamReader(getClass().getResourceAsStream("fixture.json")));
+ String expected = CharStreams.toString(new InputStreamReader(getClass().getResourceAsStream("fixture.json"), StandardCharsets.UTF_8));
TEST_CONTEXT.assertEqualJson(actual, expected, Configuration.empty().withOptions(Option.TREATING_NULL_AS_ABSENT).whenIgnoringPaths("languages[0].rtl"));
}
diff --git a/src/test/java/io/ipdata/client/MockIpdataServer.java b/src/test/java/io/ipdata/client/MockIpdataServer.java
index e8c87e9..f0e06a2 100644
--- a/src/test/java/io/ipdata/client/MockIpdataServer.java
+++ b/src/test/java/io/ipdata/client/MockIpdataServer.java
@@ -11,9 +11,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -212,7 +212,7 @@ private void handleGet(HttpExchange exchange, String path, Map p
}
private void handleBulk(HttpExchange exchange) throws IOException {
- String body = CharStreams.toString(new InputStreamReader(exchange.getRequestBody()));
+ String body = CharStreams.toString(new InputStreamReader(exchange.getRequestBody(), StandardCharsets.UTF_8));
String[] ips = mapper.readValue(body, String[].class);
ArrayNode result = mapper.createArrayNode();
for (String ip : ips) {
@@ -225,7 +225,7 @@ private void handleBulk(HttpExchange exchange) throws IOException {
}
private void sendResponse(HttpExchange exchange, int status, String body) throws IOException {
- byte[] bytes = body.getBytes("UTF-8");
+ byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(status, bytes.length);
exchange.getResponseBody().write(bytes);
@@ -243,11 +243,7 @@ private Map parseQuery(String rawQuery) {
for (String param : rawQuery.split("&")) {
String[] pair = param.split("=", 2);
if (pair.length == 2) {
- try {
- params.put(URLDecoder.decode(pair[0], "UTF-8"), URLDecoder.decode(pair[1], "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- params.put(pair[0], pair[1]);
- }
+ params.put(URLDecoder.decode(pair[0], StandardCharsets.UTF_8), URLDecoder.decode(pair[1], StandardCharsets.UTF_8));
}
}
}
diff --git a/src/test/java/io/ipdata/client/TestContext.java b/src/test/java/io/ipdata/client/TestContext.java
index c367d3b..8bf26d1 100644
--- a/src/test/java/io/ipdata/client/TestContext.java
+++ b/src/test/java/io/ipdata/client/TestContext.java
@@ -21,6 +21,7 @@
import java.io.InputStreamReader;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.Map;
import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE;
@@ -71,7 +72,8 @@ void assertEqualJson(String actual, String expected, Configuration configuration
}
/*
- Used to get responses of a raw http call, without logical proxying
+ Used to get responses of a raw http call, without logical proxying.
+ Bodies are always decoded as UTF-8, whatever the platform default charset is.
*/
@SneakyThrows
String get(String path, Map params) {
@@ -84,7 +86,7 @@ String get(String path, Map params) {
}
HttpGet httpget = new HttpGet(builder.build());
HttpResponse response = httpClient.execute(httpget);
- return CharStreams.toString(new InputStreamReader(response.getEntity().getContent()));
+ return CharStreams.toString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
}
@SneakyThrows
@@ -97,9 +99,9 @@ String post(String path, String content, Map params) {
}
}
HttpPost httpPost = new HttpPost(builder.build());
- httpPost.setEntity(new StringEntity(content));
+ httpPost.setEntity(new StringEntity(content, StandardCharsets.UTF_8));
HttpResponse response = httpClient.execute(httpPost);
- return CharStreams.toString(new InputStreamReader(response.getEntity().getContent()));
+ return CharStreams.toString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
}
diff --git a/src/test/resources/io/ipdata/client/fixture.json b/src/test/resources/io/ipdata/client/fixture.json
index f5ecb49..3ece9f8 100644
--- a/src/test/resources/io/ipdata/client/fixture.json
+++ b/src/test/resources/io/ipdata/client/fixture.json
@@ -39,7 +39,8 @@
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json b/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json
index 11e2002..3475de5 100644
--- a/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json
+++ b/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json
@@ -37,7 +37,8 @@
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/187.13.250.1.json b/src/test/resources/io/ipdata/client/fixtures/187.13.250.1.json
index 3e179dc..685d07f 100644
--- a/src/test/resources/io/ipdata/client/fixtures/187.13.250.1.json
+++ b/src/test/resources/io/ipdata/client/fixtures/187.13.250.1.json
@@ -32,7 +32,8 @@
"languages": [
{
"name": "Portuguese",
- "native": "Português"
+ "native": "Português",
+ "code": "pt"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json
index 62b19c9..fd66fe7 100644
--- a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json
+++ b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json
@@ -37,7 +37,8 @@
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json
index 766ef8c..5ba5553 100644
--- a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json
+++ b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json
@@ -37,7 +37,8 @@
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json b/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json
index 58c661a..211ff33 100644
--- a/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json
+++ b/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json
@@ -37,7 +37,8 @@
"languages": [
{
"name": "Arabic",
- "native": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"
+ "native": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629",
+ "code": "ar"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json b/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json
index 4a7013d..a3a2666 100644
--- a/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json
+++ b/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json
@@ -38,7 +38,8 @@
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en"
}
],
"currency": {
diff --git a/src/test/resources/io/ipdata/client/fixtures/98.97.34.0.json b/src/test/resources/io/ipdata/client/fixtures/98.97.34.0.json
index 2f780b5..c17c0c7 100644
--- a/src/test/resources/io/ipdata/client/fixtures/98.97.34.0.json
+++ b/src/test/resources/io/ipdata/client/fixtures/98.97.34.0.json
@@ -33,7 +33,8 @@
"languages": [
{
"name": "English",
- "native": "English"
+ "native": "English",
+ "code": "en"
}
],
"currency": {