Skip to content

Commit

Permalink
Merge pull request #230 from /issues/217-user-agent-optional
Browse files Browse the repository at this point in the history
Make UserAgent null-safe and return optional instead
  • Loading branch information
banterCZ authored Nov 16, 2023
2 parents fdfa0c3 + 11a5459 commit d331578
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -51,32 +52,37 @@ private UserAgent() {
"(?<product>[a-zA-Z0-9-_.]+)/(?<version>[0-9.]+) .*" +
"\\((?<platform>[^;]+); (?<os>[^/]+)/(?<osVersion>[^;]+); (?<model>[^)]+)\\)$");

public static Device parse(String userAgent) {
/**
* Parse client user from the HTTP header value.
*
* @param userAgent User-Agent Header String
* @return Parsed device info, or empty if the user agent header cannot be parsed.
*/
public static Optional<Device> parse(String userAgent) {
// Identify if the user agent is ours and in what version
logger.debug("Parsing user agent value: {}", userAgent);
final Matcher matcherPrefix = patternPrefix.matcher(userAgent);
if (!matcherPrefix.matches()) {
return null;
return Optional.empty();
}
final String networkVersion = matcherPrefix.group("networkVersion");
logger.debug("Declared networkVersion: {}", networkVersion);
if (!networkVersion.startsWith("1.")) { // simplistic matching for current v1.x clients
return null;
return Optional.empty();
}

// Parse the device object
return parseUserAgentV1(userAgent);

}

/**
* Private method for parsing client user from the v1.x mobile clients. It is added for convenience
* when new versions with another formats will be eventually introduced.
*
* @param userAgent User-Agent Header String
* @return Parsed device info, or null if the user agent header cannot be parsed.
* @return Parsed device info, or empty if the user agent header cannot be parsed.
*/
private static Device parseUserAgentV1(String userAgent) {
private static Optional<Device> parseUserAgentV1(String userAgent) {
final Matcher matcher = patternV1.matcher(userAgent);
if (matcher.matches()) {
final Device device = new Device();
Expand All @@ -89,10 +95,10 @@ private static Device parseUserAgentV1(String userAgent) {
device.setOs(matcher.group("os"));
device.setOsVersion(matcher.group("osVersion"));
device.setModel(matcher.group("model"));
return device;
return Optional.of(device);
}
logger.debug("The user agent value does not match v1 client format");
return null;
return Optional.empty();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

import org.junit.jupiter.api.Test;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Test for the user agent parser.
Expand All @@ -30,8 +32,10 @@ class UserAgentTest {
@Test
void parse() {
final String sample = "PowerAuthNetworking/1.1.7 (en; cellular) com.wultra.app.Mobile-Token.wultra_test/2.0.0 (Apple; iOS/16.6.1; iphone12,3)";
final UserAgent.Device device = UserAgent.parse(sample);
assertNotNull(device);
final Optional<UserAgent.Device> deviceOptional = UserAgent.parse(sample);
assertTrue(deviceOptional.isPresent());

final UserAgent.Device device = deviceOptional.get();
assertEquals("1.1.7", device.getNetworkVersion());
assertEquals("en", device.getLanguage());
assertEquals("cellular", device.getConnection());
Expand Down

0 comments on commit d331578

Please sign in to comment.