Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make UserAgent null-safe and return optional instead #230

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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