Skip to content

Commit f8a644f

Browse files
Add support to parse user-agents
1 parent b0fe739 commit f8a644f

9 files changed

Lines changed: 223 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [3.7.0] - 2022-06-10
11+
### Added
12+
- Add new `parse` method to _IpregistryClient_ to parse one or more user-agent values.
13+
1014
## [3.6.0] - 2022-04-15
1115
### Added
1216
- New `vpn` field in `security` object.
@@ -127,7 +131,8 @@ https://github.com/ipregistry/ipregistry-java#caching
127131
## [1.0.0] - 2019-07-03
128132
- First public release.
129133

130-
[Unreleased]: https://github.com/ipregistry/ipregistry-java/compare/v3.6.0...HEAD
134+
[Unreleased]: https://github.com/ipregistry/ipregistry-java/compare/v3.7.0...HEAD
135+
[3.7.0]: https://github.com/ipregistry/ipregistry-java/compare/v3.6.0...v3.7.0
131136
[3.6.0]: https://github.com/ipregistry/ipregistry-java/compare/v3.5.0...v3.6.0
132137
[3.5.0]: https://github.com/ipregistry/ipregistry-java/compare/v3.4.0...v3.5.0
133138
[3.4.0]: https://github.com/ipregistry/ipregistry-java/compare/v3.3.0...v3.4.0

src/integrationTest/java/co/ipregistry/api/client/integration/IpregistryClientIntegrationTest.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
import co.ipregistry.api.client.exceptions.ApiException;
55
import co.ipregistry.api.client.exceptions.ClientException;
66
import co.ipregistry.api.client.exceptions.IpInfoException;
7-
import co.ipregistry.api.client.model.ConnectionType;
8-
import co.ipregistry.api.client.model.IpInfo;
9-
import co.ipregistry.api.client.model.IpInfoList;
10-
import co.ipregistry.api.client.model.IpType;
7+
import co.ipregistry.api.client.model.*;
118
import co.ipregistry.api.client.options.IpregistryOptions;
129
import com.google.common.collect.ImmutableList;
1310
import com.google.common.collect.Lists;
@@ -219,4 +216,22 @@ void testOriginLookup() throws ApiException, ClientException {
219216
Assertions.assertNotNull(ipInfo.getTimeZone());
220217
}
221218

219+
@Test
220+
void testUserAgentParsing() throws ApiException, ClientException {
221+
final IpregistryClient client = new IpregistryClient(apiKey);
222+
final UserAgentList userAgentList =
223+
client.parse(
224+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36",
225+
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0",
226+
"@@@");
227+
228+
Assertions.assertEquals(3, userAgentList.size());
229+
Assertions.assertEquals("Chrome", userAgentList.get(0).getName());
230+
Assertions.assertEquals("Linux", userAgentList.get(0).getOperatingSystem().getName());
231+
Assertions.assertEquals("Firefox", userAgentList.get(1).getName());
232+
Assertions.assertEquals("Windows NT", userAgentList.get(1).getOperatingSystem().getName());
233+
Assertions.assertEquals("Hacker", userAgentList.get(2).getName());
234+
Assertions.assertEquals("Hacker", userAgentList.get(2).getOperatingSystem().getName());
235+
}
236+
222237
}

src/main/java/co/ipregistry/api/client/IpregistryClient.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@
2020
import co.ipregistry.api.client.cache.NoCache;
2121
import co.ipregistry.api.client.exceptions.ApiException;
2222
import co.ipregistry.api.client.exceptions.ClientException;
23-
import co.ipregistry.api.client.model.IpInfo;
24-
import co.ipregistry.api.client.model.IpInfoList;
25-
import co.ipregistry.api.client.model.RequesterIpInfo;
23+
import co.ipregistry.api.client.exceptions.IpregistryException;
24+
import co.ipregistry.api.client.model.*;
2625
import co.ipregistry.api.client.options.IpregistryOption;
2726
import co.ipregistry.api.client.request.DefaultRequestHandler;
2827

@@ -207,4 +206,8 @@ public IpInfoList lookup(final List<String> ips, final IpregistryOption... optio
207206
return new IpInfoList(result);
208207
}
209208

209+
public UserAgentList parse(final String... userAgents) throws ApiException, ClientException {
210+
return requestHandler.parse(userAgents);
211+
}
212+
210213
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2019 Ipregistry (https://ipregistry.co).
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package co.ipregistry.api.client.exceptions;
18+
19+
import lombok.Getter;
20+
21+
22+
/**
23+
* UserAgent exception.
24+
*/
25+
@Getter
26+
public class UserAgentException extends ApiException {
27+
28+
/**
29+
* Creates a new instance with the specified arguments.
30+
*
31+
* @param errorCode the error code.
32+
* @param message the message describing the error.
33+
* @param resolution a sentence explaining how to resolve the error.
34+
*/
35+
public UserAgentException(final String errorCode, final String message, final String resolution) {
36+
super(errorCode, message, resolution);
37+
}
38+
39+
}

src/main/java/co/ipregistry/api/client/model/UserAgentList.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@
1616

1717
package co.ipregistry.api.client.model;
1818

19+
import co.ipregistry.api.client.exceptions.UserAgentException;
1920
import com.fasterxml.jackson.annotation.JsonProperty;
21+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2022
import lombok.AllArgsConstructor;
2123
import lombok.Data;
2224
import lombok.NoArgsConstructor;
25+
import lombok.Setter;
2326

2427

2528
/**
2629
* A list of user-agent data as a result of a bulk parsing.
2730
*/
2831
@AllArgsConstructor
29-
@Data
32+
@JsonDeserialize(using = UserAgentListDeserializer.class)
33+
@Setter
3034
@NoArgsConstructor
3135
public class UserAgentList {
3236

@@ -53,4 +57,31 @@ public void set(final int index, final Object value) {
5357
userAgents[index] = value;
5458
}
5559

60+
public UserAgent get(final int index) throws UserAgentException {
61+
final Object object = userAgents[index];
62+
63+
if (object instanceof UserAgent) {
64+
return (UserAgent) object;
65+
}
66+
67+
throw (UserAgentException) object;
68+
}
69+
70+
/**
71+
* Returns the list size.
72+
*
73+
* @return the number of entries available in the list.
74+
*/
75+
public int size() {
76+
if (userAgents == null) {
77+
return 0;
78+
}
79+
80+
return userAgents.length;
81+
}
82+
83+
public Object unsafeGet(final int index) {
84+
return userAgents[index];
85+
}
86+
5687
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2022 Ipregistry (https://ipregistry.co).
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package co.ipregistry.api.client.model;
18+
19+
import co.ipregistry.api.client.exceptions.IpInfoException;
20+
import co.ipregistry.api.client.model.error.LookupError;
21+
import com.fasterxml.jackson.core.JsonParser;
22+
import com.fasterxml.jackson.core.ObjectCodec;
23+
import com.fasterxml.jackson.core.TreeNode;
24+
import com.fasterxml.jackson.databind.DeserializationContext;
25+
import com.fasterxml.jackson.databind.JsonDeserializer;
26+
27+
import java.io.IOException;
28+
29+
30+
/**
31+
* Implementation used to deserialize an {@link UserAgentList}.
32+
*/
33+
public class UserAgentListDeserializer extends JsonDeserializer<Object> {
34+
35+
/**
36+
* Creates a new instance.
37+
*/
38+
public UserAgentListDeserializer() {
39+
super();
40+
}
41+
42+
@Override
43+
public UserAgentList deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {
44+
final ObjectCodec codec = parser.getCodec();
45+
final TreeNode treeNode = codec.readTree(parser).get("results");
46+
47+
final Object[] objects = new Object[treeNode.size()];
48+
49+
for (int i = 0; i < treeNode.size(); i++) {
50+
final TreeNode userAgentOrLookupError = treeNode.get(i);
51+
52+
if (userAgentOrLookupError.get("code") == null) {
53+
objects[i] = codec.treeToValue(userAgentOrLookupError, UserAgent.class);
54+
} else {
55+
final LookupError lookupError = codec.treeToValue(userAgentOrLookupError, LookupError.class);
56+
objects[i] =
57+
new IpInfoException(
58+
lookupError.getCode(),
59+
lookupError.getMessage(),
60+
lookupError.getResolution());
61+
}
62+
}
63+
64+
return new UserAgentList(objects);
65+
}
66+
67+
}

src/main/java/co/ipregistry/api/client/request/DefaultRequestHandler.java

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import co.ipregistry.api.client.model.IpInfo;
2323
import co.ipregistry.api.client.model.IpInfoList;
2424
import co.ipregistry.api.client.model.RequesterIpInfo;
25+
import co.ipregistry.api.client.model.UserAgentList;
2526
import co.ipregistry.api.client.model.error.LookupError;
2627
import co.ipregistry.api.client.options.IpregistryOption;
2728
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -36,6 +37,8 @@
3637
import java.io.UnsupportedEncodingException;
3738
import java.net.URLEncoder;
3839
import java.nio.charset.StandardCharsets;
40+
import java.util.Arrays;
41+
import java.util.Iterator;
3942
import java.util.stream.Collectors;
4043
import java.util.stream.StreamSupport;
4144

@@ -86,7 +89,7 @@ public IpInfo lookup(final String ip, final IpregistryOption... options) throws
8689
try {
8790
final Class<? extends IpInfo> type = "".equals(ip) || ip == null ? RequesterIpInfo.class : IpInfo.class;
8891

89-
final Object result = Request.get(buildApiUrl(ip, options))
92+
final Object result = Request.get(buildIpLookupUrl(ip, options))
9093
.addHeader("authorization", "ApiKey " + config.getApiKey())
9194
.addHeader("user-agent", USER_AGENT)
9295
.connectTimeout(Timeout.ofMilliseconds(config.getConnectionTimeout()))
@@ -131,13 +134,13 @@ private ApiException createCustomException(final ClassicHttpResponse response) t
131134
}
132135

133136
/**
134-
* Crafts a new API URL for the specified {@code ip} and {@code options}.
137+
* Crafts a new URL for the specified {@code ip} and {@code options}.
135138
*
136139
* @param ip the IP address to lookup.
137140
* @param options the options to pass.
138141
* @return an API URL for the specified input arguments.
139142
*/
140-
protected String buildApiUrl(final String ip, final IpregistryOption... options) {
143+
protected String buildIpLookupUrl(final String ip, final IpregistryOption... options) {
141144
final StringBuilder result = new StringBuilder();
142145

143146
result.append(config.getBaseUrl());
@@ -165,7 +168,7 @@ protected String buildApiUrl(final String ip, final IpregistryOption... options)
165168

166169
public IpInfoList lookup(final Iterable<String> ips, final IpregistryOption... options) throws ApiException, ClientException {
167170
try {
168-
final Object result = Request.post(buildApiUrl("", options))
171+
final Object result = Request.post(buildIpLookupUrl("", options))
169172
.bodyString(toJsonList(ips), ContentType.APPLICATION_JSON)
170173
.addHeader("authorization", "ApiKey " + config.getApiKey())
171174
.addHeader("user-agent", USER_AGENT)
@@ -193,6 +196,47 @@ public IpInfoList lookup(final Iterable<String> ips, final IpregistryOption... o
193196
}
194197
}
195198

199+
@Override
200+
public UserAgentList parse(final String... userAgents) throws ApiException, ClientException {
201+
final StringBuilder buffer = new StringBuilder();
202+
final Iterator<String> iterator = Arrays.stream(userAgents).iterator();
203+
while (iterator.hasNext()) {
204+
buffer.append('"');
205+
buffer.append(iterator.next());
206+
buffer.append('"');
207+
if (iterator.hasNext()) {
208+
buffer.append(',');
209+
}
210+
}
211+
212+
try {
213+
final Object result = Request.post(config.getBaseUrl() + "/user_agent")
214+
.bodyString("[" + buffer + "]", ContentType.APPLICATION_JSON)
215+
.addHeader("authorization", "ApiKey " + config.getApiKey())
216+
.connectTimeout(Timeout.ofMilliseconds(config.getConnectionTimeout()))
217+
.responseTimeout(Timeout.ofMilliseconds(config.getSocketTimeout()))
218+
.execute().handleResponse(response -> {
219+
try {
220+
if (response.getCode() == HttpStatus.SC_OK) {
221+
return objectMapper.readValue(response.getEntity().getContent(), UserAgentList.class);
222+
} else {
223+
return createCustomException(response);
224+
}
225+
} catch (final IOException e) {
226+
return new ClientException(e);
227+
}
228+
});
229+
230+
if (result instanceof UserAgentList) {
231+
return (UserAgentList) result;
232+
}
233+
234+
throw (ApiException) result;
235+
} catch (final IOException e) {
236+
throw new ClientException(e);
237+
}
238+
}
239+
196240
private String toJsonList(final Iterable<String> ips) {
197241
return '[' + StreamSupport
198242
.stream(ips.spliterator(), false)

src/main/java/co/ipregistry/api/client/request/IpregistryRequestHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
package co.ipregistry.api.client.request;
1818

19+
import co.ipregistry.api.client.exceptions.ApiException;
20+
import co.ipregistry.api.client.exceptions.ClientException;
1921
import co.ipregistry.api.client.exceptions.IpregistryException;
2022
import co.ipregistry.api.client.model.IpInfo;
2123
import co.ipregistry.api.client.model.IpInfoList;
24+
import co.ipregistry.api.client.model.UserAgentList;
2225
import co.ipregistry.api.client.options.IpregistryOption;
2326

2427

@@ -55,4 +58,6 @@ public interface IpregistryRequestHandler {
5558
*/
5659
IpInfoList lookup(Iterable<String> ips, IpregistryOption... options) throws IpregistryException;
5760

61+
UserAgentList parse(String... userAgents) throws ApiException, ClientException;
62+
5863
}

src/test/java/co/ipregistry/api/client/request/DefaultRequestHandlerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ void testBuildApiUrl_optionsEncoded() {
3030
final DefaultRequestHandler requestHandler =
3131
new DefaultRequestHandler(config);
3232
final String url =
33-
requestHandler.buildApiUrl(
33+
requestHandler.buildIpLookupUrl(
3434
"8.8.8.8", new IpregistryOption("test", "[test]"));
3535
Assertions.assertEquals(config.getBaseUrl() + "/8.8.8.8?test=%5Btest%5D", url);
3636
}

0 commit comments

Comments
 (0)