diff --git a/CHANGES.MD b/CHANGES.MD index fdb61f4..d58650a 100644 --- a/CHANGES.MD +++ b/CHANGES.MD @@ -1,3 +1,7 @@ +2.4.0 (2018-10-22) +================= +- Add support for `$app` and `$browser` to the following events: `AddItemToCart`, `AddPromotion`, `CreateAccount`, `CreateContent`, `CreateOrder`, `CustomEvent`, `RemoveItemFromCart`, `Transaction`, `UpdateAccount`, and `UpdateContent` + 2.3.0 (2018-08-08) ================= diff --git a/build.gradle b/build.gradle index 5faa9bb..c78c123 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'org.skyscreamer', name: 'jsonassert', version: '1.3.0' testCompile group: 'com.squareup.okhttp3', name: 'mockwebserver', version: '3.4.2' + testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3' compile 'com.google.code.gson:gson:2.7' compile 'com.squareup.okhttp3:okhttp:3.4.2' compile 'com.squareup.okio:okio:1.9.0' diff --git a/src/main/java/com/siftscience/model/AddItemToCartFieldSet.java b/src/main/java/com/siftscience/model/AddItemToCartFieldSet.java index 2462a57..74f28a2 100644 --- a/src/main/java/com/siftscience/model/AddItemToCartFieldSet.java +++ b/src/main/java/com/siftscience/model/AddItemToCartFieldSet.java @@ -3,7 +3,7 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -public class AddItemToCartFieldSet extends EventsApiRequestFieldSet { +public class AddItemToCartFieldSet extends BaseAppBrowserFieldSet { public static AddItemToCartFieldSet fromJson(String json) { return gson.fromJson(json, AddItemToCartFieldSet.class); } diff --git a/src/main/java/com/siftscience/model/AddPromotionFieldSet.java b/src/main/java/com/siftscience/model/AddPromotionFieldSet.java index b913805..83fd122 100644 --- a/src/main/java/com/siftscience/model/AddPromotionFieldSet.java +++ b/src/main/java/com/siftscience/model/AddPromotionFieldSet.java @@ -5,7 +5,7 @@ import java.util.List; -public class AddPromotionFieldSet extends EventsApiRequestFieldSet { +public class AddPromotionFieldSet extends BaseAppBrowserFieldSet { public static AddPromotionFieldSet fromJson(String json) { return gson.fromJson(json, AddPromotionFieldSet.class); } diff --git a/src/main/java/com/siftscience/model/BaseAccountFieldSet.java b/src/main/java/com/siftscience/model/BaseAccountFieldSet.java index 6137482..73f32d1 100644 --- a/src/main/java/com/siftscience/model/BaseAccountFieldSet.java +++ b/src/main/java/com/siftscience/model/BaseAccountFieldSet.java @@ -6,7 +6,7 @@ import java.util.List; public abstract class BaseAccountFieldSet> - extends EventsApiRequestFieldSet { + extends BaseAppBrowserFieldSet { @Expose @SerializedName("$user_email") private String userEmail; @Expose @SerializedName("$name") private String name; @Expose @SerializedName("$phone") private String phone; diff --git a/src/main/java/com/siftscience/model/BaseAppBrowserFieldSet.java b/src/main/java/com/siftscience/model/BaseAppBrowserFieldSet.java new file mode 100644 index 0000000..058f253 --- /dev/null +++ b/src/main/java/com/siftscience/model/BaseAppBrowserFieldSet.java @@ -0,0 +1,29 @@ +package com.siftscience.model; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public abstract class BaseAppBrowserFieldSet> + extends EventsApiRequestFieldSet { + @Expose @SerializedName("$app") private App app; + @Expose @SerializedName("$browser") private Browser browser; + + public App getApp() { + return app; + } + + public T setApp(App app) { + this.app = app; + return (T) this; + } + + public Browser getBrowser() { + return browser; + } + + public T setBrowser(Browser browser) { + this.browser = browser; + return (T) this; + } + +} diff --git a/src/main/java/com/siftscience/model/BaseContentFieldSet.java b/src/main/java/com/siftscience/model/BaseContentFieldSet.java index 5c2fb69..a36eb3d 100644 --- a/src/main/java/com/siftscience/model/BaseContentFieldSet.java +++ b/src/main/java/com/siftscience/model/BaseContentFieldSet.java @@ -6,7 +6,7 @@ import java.util.List; public abstract class BaseContentFieldSet> - extends EventsApiRequestFieldSet { + extends BaseAppBrowserFieldSet { @Expose @SerializedName("$content_id") private String contentId; @Expose @SerializedName("$status") private String status; diff --git a/src/main/java/com/siftscience/model/BaseOrderFieldSet.java b/src/main/java/com/siftscience/model/BaseOrderFieldSet.java index e0811d9..5671e57 100644 --- a/src/main/java/com/siftscience/model/BaseOrderFieldSet.java +++ b/src/main/java/com/siftscience/model/BaseOrderFieldSet.java @@ -6,7 +6,7 @@ import java.util.List; public abstract class BaseOrderFieldSet> - extends EventsApiRequestFieldSet { + extends BaseAppBrowserFieldSet { @Expose @SerializedName("$order_id") private String orderId; @Expose @SerializedName("$user_email") private String userEmail; @Expose @SerializedName("$amount") private Long amount; diff --git a/src/main/java/com/siftscience/model/CustomEventFieldSet.java b/src/main/java/com/siftscience/model/CustomEventFieldSet.java index da286a0..2ae2a6f 100644 --- a/src/main/java/com/siftscience/model/CustomEventFieldSet.java +++ b/src/main/java/com/siftscience/model/CustomEventFieldSet.java @@ -3,7 +3,7 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -public class CustomEventFieldSet extends EventsApiRequestFieldSet { +public class CustomEventFieldSet extends BaseAppBrowserFieldSet { public static CustomEventFieldSet fromJson(String json) { return gson.fromJson(json, CustomEventFieldSet.class); } diff --git a/src/main/java/com/siftscience/model/LoginFieldSet.java b/src/main/java/com/siftscience/model/LoginFieldSet.java index 0a9099f..a9af45c 100644 --- a/src/main/java/com/siftscience/model/LoginFieldSet.java +++ b/src/main/java/com/siftscience/model/LoginFieldSet.java @@ -3,14 +3,12 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -public class LoginFieldSet extends EventsApiRequestFieldSet { +public class LoginFieldSet extends BaseAppBrowserFieldSet { public static LoginFieldSet fromJson(String json) { return gson.fromJson(json, LoginFieldSet.class); } @Expose @SerializedName("$login_status") private String loginStatus; - @Expose @SerializedName("$browser") private Browser browser; - @Expose @SerializedName("$app") private App app; @Override public String getEventType() { @@ -25,22 +23,4 @@ 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/RemoveItemFromCartFieldSet.java b/src/main/java/com/siftscience/model/RemoveItemFromCartFieldSet.java index a03728a..19cbcda 100644 --- a/src/main/java/com/siftscience/model/RemoveItemFromCartFieldSet.java +++ b/src/main/java/com/siftscience/model/RemoveItemFromCartFieldSet.java @@ -3,8 +3,7 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -public class RemoveItemFromCartFieldSet - extends EventsApiRequestFieldSet { +public class RemoveItemFromCartFieldSet extends BaseAppBrowserFieldSet { public static RemoveItemFromCartFieldSet fromJson(String json) { return gson.fromJson(json, RemoveItemFromCartFieldSet.class); } diff --git a/src/main/java/com/siftscience/model/TransactionFieldSet.java b/src/main/java/com/siftscience/model/TransactionFieldSet.java index 2067a7e..fad9e7d 100644 --- a/src/main/java/com/siftscience/model/TransactionFieldSet.java +++ b/src/main/java/com/siftscience/model/TransactionFieldSet.java @@ -3,7 +3,7 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -public class TransactionFieldSet extends EventsApiRequestFieldSet { +public class TransactionFieldSet extends BaseAppBrowserFieldSet { @Expose @SerializedName("$amount") private Long amount; @Expose @SerializedName("$currency_code") private String currencyCode; @Expose @SerializedName("$user_email") private String userEmail; diff --git a/src/test/java/com/siftscience/AddItemToCardEventTest.java b/src/test/java/com/siftscience/AddItemToCartEventTest.java similarity index 52% rename from src/test/java/com/siftscience/AddItemToCardEventTest.java rename to src/test/java/com/siftscience/AddItemToCartEventTest.java index 59b26d7..4b476c4 100644 --- a/src/test/java/com/siftscience/AddItemToCardEventTest.java +++ b/src/test/java/com/siftscience/AddItemToCartEventTest.java @@ -11,40 +11,41 @@ import static java.net.HttpURLConnection.HTTP_OK; -public class AddItemToCardEventTest { +public class AddItemToCartEventTest { + @Test public void testAddItemToCart() throws Exception { String expectedRequestBody = "{\n" + - " \"$type\" : \"$add_item_to_cart\",\n" + - " \"$api_key\" : \"your_api_key_here\",\n" + - " \"$user_id\" : \"billy_jones_301\",\n" + - "\n" + - " \"$session_id\" : \"gigtleqddo84l8cm15qe4il\",\n" + - " \"$item\" : {\n" + - " \"$item_id\" : \"B004834GQO\",\n" + - " \"$product_title\" : \"The Slanket Blanket-Texas Tea\",\n" + - " \"$price\" : 39990000,\n" + - " \"$upc\" : \"67862114510011\",\n" + - " \"$sku\" : \"004834GQ\",\n" + - " \"$brand\" : \"Slanket\",\n" + - " \"$manufacturer\" : \"Slanket\",\n" + - " \"$category\" : \"Blankets & Throws\",\n" + - " \"$tags\" : [\"Awesome\", \"Wintertime specials\"],\n" + - " \"$color\" : \"Texas Tea\",\n" + - " \"$quantity\" : 2\n" + - " }\n" + - "}"; + " \"$type\" : \"$add_item_to_cart\",\n" + + " \"$api_key\" : \"your_api_key_here\",\n" + + " \"$user_id\" : \"billy_jones_301\",\n" + + "\n" + + " \"$session_id\" : \"gigtleqddo84l8cm15qe4il\",\n" + + " \"$item\" : {\n" + + " \"$item_id\" : \"B004834GQO\",\n" + + " \"$product_title\" : \"The Slanket Blanket-Texas Tea\",\n" + + " \"$price\" : 39990000,\n" + + " \"$upc\" : \"67862114510011\",\n" + + " \"$sku\" : \"004834GQ\",\n" + + " \"$brand\" : \"Slanket\",\n" + + " \"$manufacturer\" : \"Slanket\",\n" + + " \"$category\" : \"Blankets & Throws\",\n" + + " \"$tags\" : [\"Awesome\", \"Wintertime specials\"],\n" + + " \"$color\" : \"Texas Tea\",\n" + + " \"$quantity\" : 2\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" + - "}"); + " \"status\" : 0,\n" + + " \"error_message\" : \"OK\",\n" + + " \"time\" : 1327604222,\n" + + " \"request\" : \"" + TestUtils.unescapeJson(expectedRequestBody) + "\"\n" + + "}"); server.enqueue(response); server.start(); HttpUrl baseUrl = server.url(""); @@ -55,9 +56,9 @@ public void testAddItemToCart() throws Exception { // Build and execute the request against the mock server. SiftRequest request = client.buildRequest(new AddItemToCartFieldSet() - .setUserId("billy_jones_301") - .setSessionId("gigtleqddo84l8cm15qe4il") - .setItem(TestUtils.sampleItem2())); + .setUserId("billy_jones_301") + .setSessionId("gigtleqddo84l8cm15qe4il") + .setItem(TestUtils.sampleItem2())); SiftResponse siftResponse = request.send(); @@ -71,7 +72,7 @@ public void testAddItemToCart() throws Exception { Assert.assertEquals(HTTP_OK, siftResponse.getHttpStatusCode()); Assert.assertEquals(0, (int) siftResponse.getBody().getStatus()); JSONAssert.assertEquals(response.getBody().readUtf8(), - siftResponse.getBody().toJson(), true); + siftResponse.getBody().toJson(), true); server.shutdown(); } diff --git a/src/test/java/com/siftscience/BaseAppBrowserFieldSetTest.java b/src/test/java/com/siftscience/BaseAppBrowserFieldSetTest.java new file mode 100644 index 0000000..cf2b3ca --- /dev/null +++ b/src/test/java/com/siftscience/BaseAppBrowserFieldSetTest.java @@ -0,0 +1,186 @@ +package com.siftscience; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.siftscience.model.AddItemToCartFieldSet; +import com.siftscience.model.AddPromotionFieldSet; +import com.siftscience.model.App; +import com.siftscience.model.BaseAppBrowserFieldSet; +import com.siftscience.model.Browser; +import com.siftscience.model.CreateAccountFieldSet; +import com.siftscience.model.CreateCommentFieldSet; +import com.siftscience.model.CreateListingFieldSet; +import com.siftscience.model.CreateMessageFieldSet; +import com.siftscience.model.CreateOrderFieldSet; +import com.siftscience.model.CreatePostFieldSet; +import com.siftscience.model.CreateProfileFieldSet; +import com.siftscience.model.CreateReviewFieldSet; +import com.siftscience.model.CustomEventFieldSet; +import com.siftscience.model.LoginFieldSet; +import com.siftscience.model.RemoveItemFromCartFieldSet; +import com.siftscience.model.TransactionFieldSet; +import com.siftscience.model.UpdateAccountFieldSet; +import com.siftscience.model.UpdateCommentFieldSet; +import com.siftscience.model.UpdateListingFieldSet; +import com.siftscience.model.UpdateMessageFieldSet; +import com.siftscience.model.UpdateOrderFieldSet; +import com.siftscience.model.UpdatePostFieldSet; +import com.siftscience.model.UpdateProfileFieldSet; +import com.siftscience.model.UpdateReviewFieldSet; +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; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.object.IsCompatibleType.typeCompatibleWith; + +public class BaseAppBrowserFieldSetTest { + + private static final String DUMMY_API_KEY = "your_api_key_here"; + private static final String DUMMY_TEST_FIELD = "test"; + private static final String DUMMY_USERID = "billy_jones_301"; + private static final String REQUEST_BODY_TEMPLATE = "{\n" + + "\"$type\" : \"$test\",\n" + + "\"$api_key\" : \"" + DUMMY_API_KEY + "\",\n" + + "\"$test_field\": \"" + DUMMY_TEST_FIELD + "\",\n" + + "\"$user_id\" : \"" + DUMMY_USERID + "\"" + + "%s" + + "\n}"; + + @Test + public void testAllSubclasses() { + Class [] subclasses = { + AddItemToCartFieldSet.class, + AddPromotionFieldSet.class, + CreateAccountFieldSet.class, + CreateCommentFieldSet.class, + CreateListingFieldSet.class, + CreateMessageFieldSet.class, + CreateOrderFieldSet.class, + CreatePostFieldSet.class, + CreateProfileFieldSet.class, + CreateReviewFieldSet.class, + CustomEventFieldSet.class, + LoginFieldSet.class, + RemoveItemFromCartFieldSet.class, + TransactionFieldSet.class, + UpdateAccountFieldSet.class, + UpdateCommentFieldSet.class, + UpdateListingFieldSet.class, + UpdateMessageFieldSet.class, + UpdateOrderFieldSet.class, + UpdatePostFieldSet.class, + UpdateProfileFieldSet.class, + UpdateReviewFieldSet.class + }; + + for (Class subclass : subclasses) { + assertThat(subclass, typeCompatibleWith(BaseAppBrowserFieldSet.class)); + } + } + + @Test + public void testApp() throws Exception { + String appName = "Calculator"; + String operatingSystem = "iOS"; + test( + new TestFieldSet().setTestField(DUMMY_TEST_FIELD) + .setUserId(DUMMY_USERID) + .setApp(new App().setAppName(appName) + .setOperatingSystem(operatingSystem)), + String.format(REQUEST_BODY_TEMPLATE, ",\n" + + " \"$app\" : {\n" + + " \"$os\" : \"" + operatingSystem + "\",\n" + + " \"$app_name\" : \"" + appName + "\"\n" + + " }\n") + ); + } + + @Test + public void testBrowser() throws Exception { + String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3)"; + test( + new TestFieldSet().setTestField(DUMMY_TEST_FIELD) + .setUserId(DUMMY_USERID) + .setBrowser(new Browser().setUserAgent(userAgent)), + String.format(REQUEST_BODY_TEMPLATE, ",\n" + + " \"$browser\" : {\n" + + " \"$user_agent\": \"" + userAgent + "\"\n" + + " }\n") + ); + } + + @Test + public void testNeitherAppNorBrowser() throws Exception { + test( + new TestFieldSet().setTestField(DUMMY_TEST_FIELD).setUserId(DUMMY_USERID), + String.format(REQUEST_BODY_TEMPLATE, "") + ); + } + + private void test(TestFieldSet t, String expectedRequestBody) throws Exception { + // 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(DUMMY_API_KEY); + client.setBaseUrl(baseUrl); + + SiftRequest request = client.buildRequest(t); + SiftResponse siftResponse = request.send(); + + // Verify the request. + RecordedRequest request1 = server.takeRequest(); + Assert.assertEquals("POST", request1.getMethod()); + Assert.assertEquals("/v205/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(); + } + + private static class TestFieldSet extends BaseAppBrowserFieldSet { + public TestFieldSet fromJson(String json) { + return gson.fromJson(json, TestFieldSet.class); + } + + @Expose + @SerializedName("$test_field") + private String test; + + @Override + public String getEventType() { + return "$test"; + } + + public String getTestField() { + return test; + } + + public TestFieldSet setTestField(String test) { + this.test = test; + return this; + } + } +}