diff --git a/CHANGES.MD b/CHANGES.MD
index 108906f..c8f468d 100644
--- a/CHANGES.MD
+++ b/CHANGES.MD
@@ -1,3 +1,9 @@
+1.4 (2017-02-28)
+=================
+
+- Add support for $verification event.
+- Add support for $app and $browser complex fields.
+
1.3.1 (2017-01-26)
==================
diff --git a/README.md b/README.md
index d22ca5a..c0fe824 100644
--- a/README.md
+++ b/README.md
@@ -11,13 +11,13 @@ Java 1.7 or later.
com.siftscience
sift-java
- 1.3.1
+ 1.4
```
### Gradle
```
dependencies {
- compile 'com.siftscience:sift-java:1.3.1'
+ compile 'com.siftscience:sift-java:1.4'
}
```
### Other
diff --git a/build.gradle b/build.gradle
index 4b34546..6d8662a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ apply plugin: 'signing'
apply plugin: 'java-library-distribution'
group = 'com.siftscience'
-version = '1.3.1'
+version = '1.4'
sourceCompatibility = 1.7
targetCompatibility = 1.7
diff --git a/src/main/java/com/siftscience/model/App.java b/src/main/java/com/siftscience/model/App.java
new file mode 100644
index 0000000..3d80add
--- /dev/null
+++ b/src/main/java/com/siftscience/model/App.java
@@ -0,0 +1,77 @@
+package com.siftscience.model;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class App {
+ @Expose @SerializedName("$os") private String operatingSystem;
+ @Expose @SerializedName("$os_version") private String operatingSystemVersion;
+ @Expose @SerializedName("$device_manufacturer") private String deviceManufacturer;
+ @Expose @SerializedName("$device_model") private String deviceModel;
+ @Expose @SerializedName("$device_unique_id") private String deviceUniqueId;
+ @Expose @SerializedName("$app_name") private String appName;
+ @Expose @SerializedName("$app_version") private String appVersion;
+
+ public String getOperatingSystem() {
+ return operatingSystem;
+ }
+
+ public App setOperatingSystem(String operatingSystem) {
+ this.operatingSystem = operatingSystem;
+ return this;
+ }
+
+ public String getOperatingSystemVersion() {
+ return operatingSystemVersion;
+ }
+
+ public App setOperatingSystemVersion(String operatingSystemVersion) {
+ this.operatingSystemVersion = operatingSystemVersion;
+ return this;
+ }
+
+ public String getDeviceManufacturer() {
+ return deviceManufacturer;
+ }
+
+ public App setDeviceManufacturer(String deviceManufacturer) {
+ this.deviceManufacturer = deviceManufacturer;
+ return this;
+ }
+
+ public String getDeviceModel() {
+ return deviceModel;
+ }
+
+ public App setDeviceModel(String deviceModel) {
+ this.deviceModel = deviceModel;
+ return this;
+ }
+
+ public String getDeviceUniqueId() {
+ return deviceUniqueId;
+ }
+
+ public App setDeviceUniqueId(String deviceUniqueId) {
+ this.deviceUniqueId = deviceUniqueId;
+ return this;
+ }
+
+ public String getAppName() {
+ return appName;
+ }
+
+ public App setAppName(String appName) {
+ this.appName = appName;
+ return this;
+ }
+
+ public String getAppVersion() {
+ return appVersion;
+ }
+
+ public App setAppVersion(String appVersion) {
+ this.appVersion = appVersion;
+ return this;
+ }
+}
diff --git a/src/main/java/com/siftscience/model/Browser.java b/src/main/java/com/siftscience/model/Browser.java
new file mode 100644
index 0000000..38a6971
--- /dev/null
+++ b/src/main/java/com/siftscience/model/Browser.java
@@ -0,0 +1,17 @@
+package com.siftscience.model;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class Browser {
+ @Expose @SerializedName("$user_agent") private String userAgent;
+
+ public String getUserAgent() {
+ return userAgent;
+ }
+
+ public Browser setUserAgent(String userAgent) {
+ this.userAgent = userAgent;
+ return this;
+ }
+}
diff --git a/src/main/java/com/siftscience/model/LoginFieldSet.java b/src/main/java/com/siftscience/model/LoginFieldSet.java
index ce9ad72..0a9099f 100644
--- a/src/main/java/com/siftscience/model/LoginFieldSet.java
+++ b/src/main/java/com/siftscience/model/LoginFieldSet.java
@@ -9,6 +9,8 @@ public static LoginFieldSet fromJson(String json) {
}
@Expose @SerializedName("$login_status") private String loginStatus;
+ @Expose @SerializedName("$browser") private Browser browser;
+ @Expose @SerializedName("$app") private App app;
@Override
public String getEventType() {
@@ -23,4 +25,22 @@ public LoginFieldSet setLoginStatus(String loginStatus) {
this.loginStatus = loginStatus;
return this;
}
+
+ public Browser getBrowser() {
+ return browser;
+ }
+
+ public LoginFieldSet setBrowser(Browser browser) {
+ this.browser = browser;
+ return this;
+ }
+
+ public App getApp() {
+ return app;
+ }
+
+ public LoginFieldSet setApp(App app) {
+ this.app = app;
+ return this;
+ }
}
diff --git a/src/main/java/com/siftscience/model/VerificationFieldSet.java b/src/main/java/com/siftscience/model/VerificationFieldSet.java
new file mode 100644
index 0000000..cc80f81
--- /dev/null
+++ b/src/main/java/com/siftscience/model/VerificationFieldSet.java
@@ -0,0 +1,46 @@
+package com.siftscience.model;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class VerificationFieldSet extends EventsApiRequestFieldSet {
+ public static VerificationFieldSet fromJson(String json) {
+ return gson.fromJson(json, VerificationFieldSet.class);
+ }
+
+ @Expose @SerializedName("$status") private String status;
+ @Expose @SerializedName("$verification_type") private String verificationType;
+ @Expose @SerializedName("$verified_value") private String verifiedValue;
+
+ @Override
+ public String getEventType() {
+ return "$verification";
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public VerificationFieldSet setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public String getVerificationType() {
+ return verificationType;
+ }
+
+ public VerificationFieldSet setVerificationType(String verificationType) {
+ this.verificationType = verificationType;
+ return this;
+ }
+
+ public String getVerifiedValue() {
+ return verifiedValue;
+ }
+
+ public VerificationFieldSet setVerifiedValue(String verifiedValue) {
+ this.verifiedValue = verifiedValue;
+ return this;
+ }
+}
diff --git a/src/test/java/com/siftscience/LoginEventTest.java b/src/test/java/com/siftscience/LoginEventTest.java
index c583746..ea27bca 100644
--- a/src/test/java/com/siftscience/LoginEventTest.java
+++ b/src/test/java/com/siftscience/LoginEventTest.java
@@ -1,5 +1,7 @@
package com.siftscience;
+import com.siftscience.model.App;
+import com.siftscience.model.Browser;
import com.siftscience.model.LoginFieldSet;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
@@ -12,13 +14,79 @@
import static java.net.HttpURLConnection.HTTP_OK;
public class LoginEventTest {
+
+ @Test
+ public void testLoginWithApp() throws Exception {
+ String operatingSystem = "iOS";
+ String appName = "Calculator";
+
+ String expectedRequestBody = "{\n" +
+ " \"$type\" : \"$login\",\n" +
+ " \"$api_key\" : \"your_api_key_here\",\n" +
+ " \"$user_id\" : \"billy_jones_301\",\n" +
+ " \"$login_status\" : \"$success\",\n" +
+ " \"$app\" : {\n" +
+ " \"$os\" : \"" + operatingSystem + "\",\n" +
+ " \"$app_name\" : \"" + appName + "\"\n" +
+ " }\n" +
+ "}";
+
+ // Start a new mock server and enqueue a mock response.
+ MockWebServer server = new MockWebServer();
+ MockResponse response = new MockResponse();
+ response.setResponseCode(HTTP_OK);
+ response.setBody("{\n" +
+ " \"status\" : 0,\n" +
+ " \"error_message\" : \"OK\",\n" +
+ " \"time\" : 1327604222,\n" +
+ " \"request\" : \"" + TestUtils.unescapeJson(expectedRequestBody) + "\"\n" +
+ "}");
+ server.enqueue(response);
+ server.start();
+ HttpUrl baseUrl = server.url("");
+
+ // Create a new client and link it to the mock server.
+ SiftClient client = new SiftClient("your_api_key_here");
+ client.setBaseUrl(baseUrl);
+
+ // Build and execute the request against the mock server.
+ SiftRequest request = client.buildRequest(new LoginFieldSet()
+ .setUserId("billy_jones_301")
+ .setLoginStatus("$success")
+ .setApp(new App()
+ .setOperatingSystem(operatingSystem)
+ .setAppName(appName)));
+
+ SiftResponse siftResponse = request.send();
+
+ // Verify the request.
+ RecordedRequest request1 = server.takeRequest();
+ Assert.assertEquals("POST", request1.getMethod());
+ Assert.assertEquals("/v204/events", request1.getPath());
+ JSONAssert.assertEquals(expectedRequestBody, request.getFieldSet().toJson(), true);
+
+ // Verify the response.
+ Assert.assertEquals(HTTP_OK, siftResponse.getHttpStatusCode());
+ Assert.assertEquals(0, (int) siftResponse.getBody().getStatus());
+ JSONAssert.assertEquals(response.getBody().readUtf8(),
+ siftResponse.getBody().toJson(), true);
+
+ server.shutdown();
+
+ }
+
@Test
- public void testLogin() throws Exception {
+ public void testLoginWithBrowswer() throws Exception {
+ String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3)";
+
String expectedRequestBody = "{\n" +
" \"$type\" : \"$login\",\n" +
" \"$api_key\" : \"your_api_key_here\",\n" +
" \"$user_id\" : \"billy_jones_301\",\n" +
- " \"$login_status\" : \"$success\"\n" +
+ " \"$login_status\" : \"$success\",\n" +
+ " \"$browser\" : {\n" +
+ " \"$user_agent\" : \"" + userAgent + "\"\n" +
+ " }\n" +
"}";
// Start a new mock server and enqueue a mock response.
@@ -42,7 +110,9 @@ public void testLogin() throws Exception {
// Build and execute the request against the mock server.
SiftRequest request = client.buildRequest(new LoginFieldSet()
.setUserId("billy_jones_301")
- .setLoginStatus("$success"));
+ .setLoginStatus("$success")
+ .setBrowser(new Browser()
+ .setUserAgent(userAgent)));
SiftResponse siftResponse = request.send();
diff --git a/src/test/java/com/siftscience/VerificationEventTest.java b/src/test/java/com/siftscience/VerificationEventTest.java
new file mode 100644
index 0000000..ebbe175
--- /dev/null
+++ b/src/test/java/com/siftscience/VerificationEventTest.java
@@ -0,0 +1,74 @@
+package com.siftscience;
+
+import com.siftscience.model.VerificationFieldSet;
+import okhttp3.HttpUrl;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+import static java.net.HttpURLConnection.HTTP_OK;
+
+public class VerificationEventTest {
+
+ @Test
+ public void testVerification() throws Exception {
+ String sessionId = "gigtleqddo84l8cm15qe4il";
+ String verifiedValue = "14155551212";
+
+ String expectedRequestBody = "{\n" +
+ " \"$type\" : \"$verification\",\n" +
+ " \"$api_key\" : \"your_api_key_here\",\n" +
+ " \"$user_id\" : \"billy_jones_301\",\n" +
+ " \"$session_id\" : \"" + sessionId + "\",\n" +
+ " \"$status\" : \"$pending\",\n" +
+ " \"$verification_type\" : \"$sms\",\n" +
+ " \"$verified_value\" : \"" + verifiedValue + "\"\n" +
+ "}";
+
+ // Start a new mock server and enqueue a mock response.
+ MockWebServer server = new MockWebServer();
+ MockResponse response = new MockResponse();
+ response.setResponseCode(HTTP_OK);
+ response.setBody("{\n" +
+ " \"status\" : 0,\n" +
+ " \"error_message\" : \"OK\",\n" +
+ " \"time\" : 1327604222,\n" +
+ " \"request\" : \"" + TestUtils.unescapeJson(expectedRequestBody) + "\"\n" +
+ "}");
+ server.enqueue(response);
+ server.start();
+ HttpUrl baseUrl = server.url("");
+
+ // Create a new client and link it to the mock server.
+ SiftClient client = new SiftClient("your_api_key_here");
+ client.setBaseUrl(baseUrl);
+
+ // Build and execute the request against the mock server.
+ SiftRequest request = client.buildRequest(new VerificationFieldSet()
+ .setUserId("billy_jones_301")
+ .setSessionId(sessionId)
+ .setStatus("$pending")
+ .setVerificationType("$sms")
+ .setVerifiedValue(verifiedValue));
+
+ SiftResponse siftResponse = request.send();
+
+ // Verify the request.
+ RecordedRequest request1 = server.takeRequest();
+ Assert.assertEquals("POST", request1.getMethod());
+ Assert.assertEquals("/v204/events", request1.getPath());
+ JSONAssert.assertEquals(expectedRequestBody, request.getFieldSet().toJson(), true);
+
+ // Verify the response.
+ Assert.assertEquals(HTTP_OK, siftResponse.getHttpStatusCode());
+ Assert.assertEquals(0, (int) siftResponse.getBody().getStatus());
+ JSONAssert.assertEquals(response.getBody().readUtf8(),
+ siftResponse.getBody().toJson(), true);
+
+ server.shutdown();
+
+ }
+}