@@ -297,7 +298,7 @@
org.codehaus.mojo
versions-maven-plugin
- 2.7
+ 2.18.0
diff --git a/src/main/java/io/castle/client/Castle.java b/src/main/java/io/castle/client/Castle.java
index 61cc4ee9..27618328 100644
--- a/src/main/java/io/castle/client/Castle.java
+++ b/src/main/java/io/castle/client/Castle.java
@@ -9,6 +9,7 @@
import io.castle.client.internal.config.CastleSdkInternalConfiguration;
import io.castle.client.internal.json.CastleGsonModel;
import io.castle.client.internal.utils.CastleContextBuilder;
+import io.castle.client.internal.utils.Webhook;
import io.castle.client.model.CastleResponse;
import io.castle.client.model.CastleSdkConfigurationException;
import org.slf4j.Logger;
@@ -17,26 +18,26 @@
import jakarta.servlet.http.HttpServletRequest;
/**
- * Creates an instance of the Castle SDK
- *
- * This also provides methods for initialization of the SDK.
- * {@code this#initialize()} must be called once per instance of the SDK
- *
- * Static method {@code this#setSingletonInstance()} can be called to set a global instance of the SDK.
- * Once set the {@code this#instance()} method will return that instance
+ * Creates an instance of the Castle SDK.
+ *
+ * Initialize once with {@link #initialize()} or {@link #initialize(CastleConfiguration)},
+ * then call {@link #client()} or {@link #onRequest(HttpServletRequest)} to send requests.
+ * {@code Castle} implements {@link AutoCloseable}; call {@link #close()} to release the HTTP client.
+ *
+ * Static method {@link #setSingletonInstance(Castle)} can be called to set a global instance of the SDK.
+ * Once set, {@link #instance()} returns that instance.
*/
-public class Castle {
- public static final String URL_TRACK = "/v1/track";
- public static final String URL_AUTHENTICATE = "/v1/authenticate";
- public static final String URL_DEVICES = "/v1/devices/";
- public static final String URL_USERS = "/v1/users/";
- public static final String URL_IMPERSONATE = "/v1/impersonate";
+public class Castle implements AutoCloseable {
public static final String URL_PRIVACY = "/v1/privacy/";
public static final String URL_RISK = "/v1/risk";
public static final String URL_FILTER = "/v1/filter";
public static final String URL_LOG = "/v1/log";
+ public static final String URL_EVENTS = "/v1/events";
- public static final String URL_RECOVER = "/v1/users/%s/recover";
+ /**
+ * Header used by Castle to sign webhook payloads.
+ */
+ public static final String WEBHOOK_SIGNATURE_HEADER = "X-Castle-Signature";
public static final String URL_LISTS = "/v1/lists";
public static final String URL_LISTS_ID = "/v1/lists/%s";
@@ -123,8 +124,11 @@ public CastleApi client() throws IllegalStateException {
}
/**
- * Creates a API client instance for sending a request
- * @param doNotTrack when true, the API calls will be not realized and default values will be provided
+ * Creates an API client instance for sending a request.
+ * The {@code doNotTrack} flag is stored on the returned client and is not read by
+ * {@code risk}, {@code filter}, {@code log}, or other API methods.
+ *
+ * @param doNotTrack tracking flag stored on the returned client
* @return A new instance of the API client {@code CastleApiImpl}
* @throws IllegalStateException when the SDK has not been properly initialized
*/
@@ -213,8 +217,8 @@ public CastleApi buildApiClient(boolean doNotTrack) {
}
/**
- * Create a API context for the given request.
- * Tracking is ON by default.
+ * Creates an API client and extracts servlet headers and IP into an internal context object.
+ * {@code risk}, {@code filter}, and {@code log} send the payload as given; set {@code context} on the payload.
*
* @param request The request for data extraction
* @return a API reference to make backend calls to the castle.io rest api.
@@ -224,10 +228,12 @@ public CastleApi onRequest(HttpServletRequest request) {
}
/**
- * Create a API context for the given request.
+ * Creates an API client and extracts servlet headers and IP into an internal context object.
+ * {@code risk}, {@code filter}, and {@code log} send the payload as given; set {@code context} on the payload.
+ * The {@code doNotTrack} flag is stored on the returned client and is not read by those methods.
*
* @param request The request for data extraction
- * @param doNotTrack when true, the API calls will be not realized and default values will be provided
+ * @param doNotTrack tracking flag stored on the returned client
* @return a API reference to make backend calls to the castle.io rest api.
*/
public CastleApi onRequest(HttpServletRequest request, boolean doNotTrack) {
@@ -271,7 +277,39 @@ public String secureUserID(String userId) {
}
/**
- * Make a GET request to a Castle API endpoint such as /v1/{userId}/devices
+ * Verifies a Castle webhook signature against the raw request body.
+ *
+ * Castle signs every webhook with an HMAC-SHA256 of the raw request body using
+ * the account API secret, base64 encoded and sent in the
+ * {@code X-Castle-Signature} header.
+ *
+ * @param signature the value of the {@code X-Castle-Signature} header
+ * @param body the raw request body bytes
+ * @return {@code true} when the signature matches the computed signature
+ */
+ public boolean verifyWebhookSignature(String signature, byte[] body) {
+ return Webhook.verifySignature(internalConfiguration.getConfiguration().getApiSecret(), body, signature);
+ }
+
+ /**
+ * Verifies a Castle webhook signature for the given servlet request.
+ *
+ * The signature is read from the {@code X-Castle-Signature} header and verified
+ * against the supplied raw request body bytes.
+ *
+ * @param request the incoming webhook request
+ * @param body the raw request body bytes
+ * @return {@code true} when the signature matches the computed signature
+ */
+ public boolean verifyWebhookSignature(HttpServletRequest request, byte[] body) {
+ if (request == null) {
+ return false;
+ }
+ return verifyWebhookSignature(request.getHeader(WEBHOOK_SIGNATURE_HEADER), body);
+ }
+
+ /**
+ * Make a GET request to a Castle API endpoint such as /v1/lists
*
* @param path api path
* @return a decoded json response
@@ -281,7 +319,7 @@ public CastleResponse get(String path) {
}
/**
- * Make a POST request to a Castle API endpoint such as /v1/track
+ * Make a POST request to a Castle API endpoint such as /v1/risk
*
* @param path api path
* @param payload request payload
@@ -291,7 +329,7 @@ public CastleResponse post(String path, ImmutableMap payload) {
return client().post(path, payload);
}
/**
- * Make a PUT request to a Castle API endpoint such as /v1/devices/{deviceToken}/report
+ * Make a PUT request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @return a decoded json response
@@ -301,7 +339,7 @@ public CastleResponse put(String path) {
}
/**
- * Make a PUT request to a Castle API endpoint such as /v1/devices/{deviceToken}/report
+ * Make a PUT request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @param payload request payload
@@ -312,7 +350,7 @@ public CastleResponse put(String path, ImmutableMap payload) {
}
/**
- * Make a DELETE request to a Castle API endpoint such as /v1/impersonate
+ * Make a DELETE request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @return a decoded json response
@@ -322,7 +360,7 @@ public CastleResponse delete(String path) {
}
/**
- * Make a DELETE request to a Castle API endpoint such as /v1/impersonate
+ * Make a DELETE request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @param payload request payload
@@ -331,4 +369,16 @@ public CastleResponse delete(String path) {
public CastleResponse delete(String path, ImmutableMap payload) {
return client().delete(path, payload);
}
+
+ /**
+ * Releases the HTTP dispatcher and connection pool used by this instance.
+ * When this instance is the SDK singleton, {@link #instance()} is cleared.
+ */
+ @Override
+ public void close() {
+ internalConfiguration.close();
+ if (instance == this) {
+ instance = null;
+ }
+ }
}
diff --git a/src/main/java/io/castle/client/api/CastleApi.java b/src/main/java/io/castle/client/api/CastleApi.java
index ae782d8d..7a7bd43d 100644
--- a/src/main/java/io/castle/client/api/CastleApi.java
+++ b/src/main/java/io/castle/client/api/CastleApi.java
@@ -1,47 +1,20 @@
package io.castle.client.api;
import com.google.common.collect.ImmutableMap;
-import com.google.gson.JsonElement;
import io.castle.client.model.*;
import io.castle.client.model.generated.*;
-import javax.annotation.Nullable;
import java.util.List;
/**
- * Contains methods for calling the Castle API and the settings needed to properly make such a request.
- *
- * Methods of this interface can be used to make calls to Castle's API
- * {@code /v1/authenticate} and {@code /v1/track} endpoints.
- *
- * A {@code castleApi} contains all necessary configurations to correctly call the Castle API.
- * In particular, it contains:
- *
- * a configuration object containing;
- * an HTTP layer for handling HTTP requests and responses;
- * a context object with metadata on the request made by the user to the server containing this client.
- *
- * The context object could have one of the following origins:
- *
- * the default setting for a context object;
- * T
- * A context object with metadata on the request made by the user to the server containing this client.
- *
- * An instance of {@code CastleApi} contains a boolean named doNotTrack in a private field.
- * When doNotTrack is set to false, the CastleApi instance created is configured to make requests when any
- * of its methods is called.
- * When set to true, authenticate and track methods return immediately without making any request.
- * The {@code this#authenticate} method will resort to the {@link io.castle.client.model.AuthenticateFailoverStrategy}
- * with authenticate action set to {@link io.castle.client.model.AuthenticateAction#ALLOW}.
- * When doNotTrack is set to true, this will resort to the {@link io.castle.client.model.AuthenticateFailoverStrategy}
- * with authenticate action set to {@link io.castle.client.model.AuthenticateAction#ALLOW}.
+ * Client for the Castle REST API.
*/
public interface CastleApi {
/**
* Merges an additional context object with the context object associated with this {@code CastleApi} instance.
- *
- * When the additional context is null, then the returned context is an empty JSON object.
+ * The merged object is stored on the returned client and is not attached to {@code risk}, {@code filter}, or {@code log} payloads.
+ * When the additional context is null, then the stored context is an empty JSON object.
*
* @param additionalContext client defined model, takes null
* @return an API reference with the merged context values
@@ -49,260 +22,68 @@ public interface CastleApi {
CastleApi mergeContext(Object additionalContext);
/**
- * Makes a sync POST request to the authenticate endpoint containing all required parameters.
- *
- * Optional parameters (that is, traits and properties) are set to null.
+ * Returns a client that stores the do-not-track flag.
+ * The flag is not read by {@code risk}, {@code filter}, {@code log}, or other API methods.
*
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID associated to an authentication attempt
- * @return a verdict that might result from a successful call to the Castle API or from the client's
- * {@link io.castle.client.model.AuthenticateFailoverStrategy}, in case of a failed call
- * @see The docs
- */
- Verdict authenticate(String event, String userId);
-
- /**
- * Makes a sync POST request to the authenticate endpoint containing required and optional parameters.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID associated to an authentication attempt
- * @param properties object for recording additional information connected to the event, takes null
- * @param traits object for recording additional information connected to the user, takes null
- * @return a verdict that might result from a successful call to the Castle API or from the client's
- * {@link io.castle.client.model.AuthenticateFailoverStrategy}, in case of a failed call
- * @see The docs
- */
- Verdict authenticate(String event, String userId, @Nullable Object properties, @Nullable Object traits);
-
- /**
- * Makes a sync POST request to the authenticate endpoint containing required and optional parameters.
- * @param message Event parameters
- * @return a verdict that might result from a successful call to the Castle API or from the client's
- */
- Verdict authenticate(CastleMessage message);
-
- JsonElement buildAuthenticateRequest(CastleMessage request);
-
- Verdict sendAuthenticateRequest(JsonElement request);
-
- void sendAuthenticateRequest(JsonElement request, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- * Makes an async POST request to the authenticate endpoint containing required and optional parameters.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID associated to an authentication attempt
- * @param properties object for recording additional information connected to the event, takes null
- * @param traits object for recording additional information connected to the user, takes null
- * @param asyncCallbackHandler a user-implemented instance of {@code AsyncCallbackHandler} which specifies
- * how to handle success of failure of authenticate API calls
- * @see The docs
- */
- void authenticateAsync(String event, String userId, @Nullable Object properties, @Nullable Object traits, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- * Makes an async POST request to the authenticate endpoint containing all required parameters.
- *
- * Optional parameters (that is, traits and properties) are set to null.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID associated to an authentication attempt
- * @param asyncCallbackHandler a user-implemented instance of {@code AsyncCallbackHandler} which specifies
- * how to handle success of failure of authenticate API calls
- * @see The docs
- */
- void authenticateAsync(String event, String userId, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- * Makes an async POST request to the authenticate endpoint containing required and optional parameters.
- * @param message Event parameters
- * @param asyncCallbackHandler a user-implemented instance of {@code AsyncCallbackHandler} which specifies
- * how to handle success of failure of authenticate API calls
- */
- void authenticateAsync(CastleMessage message, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- * Sets the doNotTrack boolean of a new instance of {@code CastleApi}
- *
- * @param doNotTrack boolean representing the value that the doNotTrack private field of the new instance of
- * {@code CastleApi}
- * @return a {@code castleApi} reference whose doNotTrack private field is set to the doNotTrack parameter
+ * @param doNotTrack boolean stored on the returned client
+ * @return a {@code CastleApi} reference that stores the given flag
*/
CastleApi doNotTrack(boolean doNotTrack);
- /**
- * Makes an async POST request to the track endpoint containing all required parameters.
- *
- * @param event a String representing an event understood by the Castle API
- * @see The docs
- */
- void track(String event);
-
- /**
- * Makes an async POST request to the track endpoint containing all required parameters and a user ID.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID
- * @see The docs
- */
- void track(String event, @Nullable String userId);
-
- /**
- * Makes an async POST request to the track endpoint containing all required parameters and a user ID.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID
- * @param reviewId a String representing a reference to review ID
- * @see The docs
- */
- void track(String event, @Nullable String userId, @Nullable String reviewId);
-
- /**
- * Makes an async POST request to the track endpoint containing required and optional parameters.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID
- * @param reviewId a String representing a reference to review ID
- * @param properties object for recording additional information connected to the event, takes null
- * @see The docs
- */
- void track(String event, @Nullable String userId, @Nullable String reviewId, @Nullable Object properties);
-
- /**
- * Makes an async POST request to the track endpoint containing required and optional parameters.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID
- * @param reviewId a String representing a reference to review ID
- * @param properties object for recording additional information connected to the event, takes null
- * @param traits object for recording additional information about the user like email or name, takes null
- * @see The docs
- */
- void track(String event, @Nullable String userId, @Nullable String reviewId, @Nullable Object properties, @Nullable Object traits);
-
- /**
- * Makes an async POST request to the track endpoint containing required and optional parameters and a custom handler
- * for the async call's success and failure cases.
- *
- * @param event a String representing an event understood by the Castle API
- * @param userId a String representing a user ID
- * @param reviewId a String representing a reference to review ID
- * @param properties object for recording additional information connected to the event, takes null
- * @param traits object for recording additional information about the user like email or name, takes null
- * @param asyncCallbackHandler a user-implemented instance of {@code AsyncCallbackHandler} which specifies
- * how to handle success of failure of authenticate API calls
- * @see The docs
- */
- void track(String event, @Nullable String userId, @Nullable String reviewId, @Nullable Object properties, @Nullable Object traits, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- * Makes an async POST request to the track endpoint containing required and optional parameters.
- * @param message Event parameters
- */
- void track(CastleMessage message);
-
- JsonElement buildTrackRequest(CastleMessage request);
-
- void sendTrackRequest(JsonElement request);
-
- void sendTrackRequest(JsonElement request, AsyncCallbackHandler asyncCallbackHandler);
+ CastleResponse get(String path);
- /**
- * Makes an async POST request to the track endpoint containing required and optional parameters.
- * @param message Event parameters
- * @param asyncCallbackHandler a user-implemented instance of {@code AsyncCallbackHandler} which specifies
- * how to handle success of failure of authenticate API calls
- */
- void track(CastleMessage message, AsyncCallbackHandler asyncCallbackHandler);
+ CastleResponse post(String path, Object payload);
- /**
- * Makes a DELETE request to the privacy endpoint.
- *
- * @param userId String representing a user id
- * @see The docs
- */
- Boolean removeUser(String userId);
+ CastleResponse put(String path);
- /**
- * Makes a sync POST request to the approve device endpoint.
- *
- * @param deviceToken string representing the device to approve
- * @return device model object
- */
- CastleUserDevice approve(String deviceToken);
+ CastleResponse put(String path, Object payload);
- /**
- * Makes a sync POST request to the report device endpoint.
- *
- * @param deviceToken string representing the device to report
- * @return device model object
- */
- CastleUserDevice report(String deviceToken);
+ CastleResponse delete(String path);
- /**
- * Makes a sync GET request to the user devices endpoint.
- *
- * @param userId user unique ID
- * @return devices model object
- */
- CastleUserDevices userDevices(String userId);
+ CastleResponse delete(String path, Object payload);
/**
- * Makes a sync GET request to the device endpoint.
+ * Makes a sync POST request to the privacy endpoint to request a user's data.
*
- * @param deviceToken string representing the device to report
- * @return device model object
+ * @param payload request parameters
+ * @return a decoded json response
*/
- CastleUserDevice device(String deviceToken);
+ CastleResponse requestUserData(ImmutableMap payload);
/**
- * Makes a sync POST request to the impersonate endpoint.
+ * Makes a sync DELETE request to the privacy endpoint to delete a user's data.
*
- * @param userId user unique ID
- * @return
+ * @param payload request parameters
+ * @return a decoded json response
*/
- CastleSuccess impersonateStart(String userId);
+ CastleResponse deleteUserData(ImmutableMap payload);
/**
- * Makes a sync POST request to the impersonate endpoint.
+ * Makes a sync GET request to the events schema endpoint.
*
- * @param userId user unique ID
- * @param impersonator description of impersonator, e.g., email
- * @return
+ * @return a decoded json response
*/
- CastleSuccess impersonateStart(String userId, String impersonator);
+ CastleResponse eventsSchema();
/**
- * Makes a sync DELETE request to the impersonate endpoint.
+ * Makes a sync POST request to the events query endpoint.
*
- * @param userId user unique ID
- * @return
+ * @param payload query parameters
+ * @return a decoded json response
*/
- CastleSuccess impersonateEnd(String userId);
+ CastleResponse queryEvents(ImmutableMap payload);
/**
- * Makes a sync DELETE request to the impersonate endpoint.
+ * Makes a sync POST request to the events group endpoint.
*
- * @param userId user unique ID
- * @param impersonator description of impersonator, e.g., email
- * @return
+ * @param payload group parameters
+ * @return a decoded json response
*/
- CastleSuccess impersonateEnd(String userId, String impersonator);
-
- CastleResponse get(String path);
-
- CastleResponse post(String path, Object payload);
-
- CastleResponse put(String path);
-
- CastleResponse put(String path, Object payload);
-
- CastleResponse delete(String path);
-
- CastleResponse delete(String path, Object payload);
+ CastleResponse groupEvents(ImmutableMap payload);
/**
- * Makes a sync POST request to the risk endpoint.
+ * Sends a POST request to {@code /v1/risk}.
+ * The payload is sent as given. Set {@code context} on the payload for IP and headers.
*
* @param payload Event parameters
* @return
@@ -310,7 +91,8 @@ public interface CastleApi {
CastleResponse risk(ImmutableMap payload);
/**
- * Makes a sync POST request to the risk endpoint.
+ * Sends a POST request to {@code /v1/risk}.
+ * The payload is sent as given. Set {@code context} on the payload for IP and headers.
*
* @param payload Event parameters
* @return
@@ -421,7 +203,8 @@ public interface CastleApi {
CastleResponse unarchiveListItem(String listId, String itemid);
/**
- * Makes a sync POST request to the filter endpoint.
+ * Sends a POST request to {@code /v1/filter}.
+ * The payload is sent as given. Set {@code context} on the payload for IP and headers.
*
* @param payload Event parameters
* @return
@@ -429,7 +212,8 @@ public interface CastleApi {
CastleResponse filter(ImmutableMap payload);
/**
- * Makes a sync POST request to the filter endpoint.
+ * Sends a POST request to {@code /v1/filter}.
+ * The payload is sent as given. Set {@code context} on the payload for IP and headers.
*
* @param payload Event parameters
* @return
@@ -437,7 +221,8 @@ public interface CastleApi {
FilterAndRiskResponse filter(Filter payload);
/**
- * Makes a sync POST request to the log endpoint.
+ * Sends a POST request to {@code /v1/log}.
+ * The payload is sent as given. Set {@code context} on the payload for IP and headers.
*
* @param payload Event parameters
* @return
@@ -445,18 +230,11 @@ public interface CastleApi {
CastleResponse log(ImmutableMap payload);
/**
- * Makes a sync POST request to the log endpoint.
+ * Sends a POST request to {@code /v1/log}.
+ * The payload is sent as given. Set {@code context} on the payload for IP and headers.
*
* @param payload Event parameters
* @return
*/
CastleResponse log(Log payload);
-
- /**
- * Makes a sync PUT request to the recover endpoint.
- *
- * @param userId User ID
- * @return
- */
- CastleResponse recover(String userId);
}
diff --git a/src/main/java/io/castle/client/internal/CastleApiImpl.java b/src/main/java/io/castle/client/internal/CastleApiImpl.java
index aaa96527..5e5cdb1f 100644
--- a/src/main/java/io/castle/client/internal/CastleApiImpl.java
+++ b/src/main/java/io/castle/client/internal/CastleApiImpl.java
@@ -2,7 +2,6 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
-import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import io.castle.client.Castle;
@@ -11,13 +10,10 @@
import io.castle.client.internal.config.CastleSdkInternalConfiguration;
import io.castle.client.internal.utils.CastleContextBuilder;
import io.castle.client.internal.utils.ContextMerge;
-import io.castle.client.internal.utils.Timestamp;
-import io.castle.client.internal.utils.VerdictBuilder;
import io.castle.client.model.*;
import io.castle.client.model.generated.*;
import jakarta.servlet.http.HttpServletRequest;
-import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.List;
@@ -70,258 +66,79 @@ public CastleApi doNotTrack(boolean doNotTrack) {
}
@Override
- public Verdict authenticate(String event, String userId) {
- return authenticate(event, userId, null, null);
- }
-
- @Override
- public Verdict authenticate(String event, String userId, @Nullable Object properties, @Nullable Object traits) {
- return authenticate(buildMessage(event, userId, properties, traits));
- }
-
- @Override
- public Verdict authenticate(CastleMessage message) {
- JsonElement request = buildAuthenticateRequest(message);
- return sendAuthenticateRequest(request);
- }
-
- @Override
- public JsonElement buildAuthenticateRequest(CastleMessage message) {
- return buildJson(message);
- }
-
- @Override
- public Verdict sendAuthenticateRequest(JsonElement request) {
- Preconditions.checkNotNull(request, "Request json can not be null");
-
- if (doNotTrack) {
- return buildVerdictForDoNotTrack(request.getAsJsonObject().get("user_id").getAsString());
- }
-
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendAuthenticateSync(request);
- }
-
- @Override
- public void sendAuthenticateRequest(JsonElement request, AsyncCallbackHandler asyncCallbackHandler) {
- Preconditions.checkNotNull(request, "Request json can not be null");
-
- if (doNotTrack) {
- asyncCallbackHandler.onResponse(buildVerdictForDoNotTrack(request.getAsJsonObject().get("user_id").getAsString()));
- } else {
- Preconditions.checkNotNull(asyncCallbackHandler, "The async handler can not be null");
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- restApi.sendAuthenticateAsync(request, asyncCallbackHandler);
- }
- }
-
- private Verdict buildVerdictForDoNotTrack(String userId) {
- return VerdictBuilder.failover("Castle set to do not track.")
- .withAction(AuthenticateAction.ALLOW)
- .withUserId(userId)
- .build();
- }
-
- @Override
- public void authenticateAsync(String event, String userId, @Nullable Object properties, @Nullable Object traits, AsyncCallbackHandler asyncCallbackHandler) {
- authenticateAsync(
- buildMessage(event, userId, properties, traits),
- asyncCallbackHandler
- );
- }
-
- @Override
- public void authenticateAsync(String event, String userId, AsyncCallbackHandler asyncCallbackHandler) {
- authenticateAsync(
- CastleMessage.builder(event).userId(userId).build(),
- asyncCallbackHandler
- );
- }
-
- @Override
- public void authenticateAsync(CastleMessage message, AsyncCallbackHandler asyncCallbackHandler) {
- JsonElement request = buildAuthenticateRequest(message);
- sendAuthenticateRequest(request, asyncCallbackHandler);
- }
-
- @Override
- public void track(String event) {
- track(event, null, null, null, null);
- }
-
- @Override
- public void track(String event, String userId) {
- track(event, userId, null, null, null);
- }
-
- @Override
- public void track(String event, @Nullable String userId, @Nullable String reviewId) {
- track(event, userId, reviewId, null, null, null);
- }
-
- @Override
- public void track(String event, String userId, String reviewId, Object properties) {
- track(event, userId, reviewId, properties, null, null);
- }
-
- @Override
- public void track(String event, @Nullable String userId, @Nullable String reviewId, @Nullable Object properties, @Nullable Object traits) {
- track(event, userId, reviewId, properties, traits, null);
- }
-
- @Override
- public void track(String event, @Nullable String userId, @Nullable String reviewId, @Nullable Object properties, @Nullable Object traits, AsyncCallbackHandler asyncCallbackHandler) {
-
- CastleMessage message = buildMessage(event, userId, properties, traits);
-
- if (reviewId != null) {
- message.setReviewId(reviewId);
- }
-
- track(message, asyncCallbackHandler);
- }
-
- @Override
- public void track(CastleMessage message) {
- track(message, null);
- }
-
- @Override
- public JsonElement buildTrackRequest(CastleMessage message) {
- Preconditions.checkNotNull(message.getEvent());
- return buildJson(message);
- }
-
- @Override
- public void sendTrackRequest(JsonElement request) {
- sendTrackRequest(request, null);
- }
-
- @Override
- public void sendTrackRequest(JsonElement request, AsyncCallbackHandler asyncCallbackHandler) {
- Preconditions.checkNotNull(request, "Request json can not be null");
-
- if (doNotTrack) {
- if (asyncCallbackHandler != null) {
- asyncCallbackHandler.onResponse(true);
- }
- return;
- }
-
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- restApi.sendTrackRequest(request, asyncCallbackHandler);
- }
-
- @Override
- public void track(CastleMessage message, @Nullable AsyncCallbackHandler asyncCallbackHandler) {
- JsonElement messageJson = buildTrackRequest(message);
-
- sendTrackRequest(messageJson, asyncCallbackHandler);
- }
-
- @Override
- public Boolean removeUser(String userId) {
- Preconditions.checkNotNull(userId);
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendPrivacyRemoveUser(userId);
- }
-
- @Override
- public CastleUserDevice approve(String deviceToken) {
- Preconditions.checkNotNull(deviceToken);
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendApproveDeviceRequestSync(deviceToken);
- }
-
- @Override
- public CastleUserDevice report(String deviceToken) {
- Preconditions.checkNotNull(deviceToken);
+ public CastleResponse get(String path) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendReportDeviceRequestSync(deviceToken);
+ return restApi.get(path);
}
@Override
- public CastleUserDevices userDevices(String userId) {
- Preconditions.checkNotNull(userId);
+ public CastleResponse post(String path, Object payload) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendGetUserDevicesRequestSync(userId);
+ return restApi.post(path, payload);
}
@Override
- public CastleUserDevice device(String deviceToken) {
- Preconditions.checkNotNull(deviceToken);
+ public CastleResponse put(String path) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendGetUserDeviceRequestSync(deviceToken);
+ return restApi.put(path);
}
@Override
- public CastleSuccess impersonateStart(String userId) {
- Preconditions.checkNotNull(userId);
+ public CastleResponse put(String path, Object payload) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendImpersonateStartRequestSync(userId, null, contextJson);
+ return restApi.put(path, payload);
}
@Override
- public CastleSuccess impersonateStart(String userId, String impersonator) {
- Preconditions.checkNotNull(userId);
+ public CastleResponse delete(String path) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendImpersonateStartRequestSync(userId, impersonator, contextJson);
+ return restApi.delete(path);
}
@Override
- public CastleSuccess impersonateEnd(String userId) {
- Preconditions.checkNotNull(userId);
+ public CastleResponse delete(String path, Object payload) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendImpersonateEndRequestSync(userId, "", contextJson);
+ return restApi.delete(path, payload);
}
- @Override
- public CastleSuccess impersonateEnd(String userId, String impersonator) {
- Preconditions.checkNotNull(userId);
+ public CastleResponse risk(ImmutableMap payload) {
+ Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.sendImpersonateEndRequestSync(userId, impersonator, contextJson);
+ return restApi.post(Castle.URL_RISK, payload);
}
@Override
- public CastleResponse get(String path) {
+ public CastleResponse requestUserData(ImmutableMap payload) {
+ Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.get(path);
+ return restApi.post(Castle.URL_PRIVACY + "users", payload);
}
@Override
- public CastleResponse post(String path, Object payload) {
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.post(path, payload);
- }
-
- @Override
- public CastleResponse put(String path) {
+ public CastleResponse deleteUserData(ImmutableMap payload) {
+ Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.put(path);
+ return restApi.delete(Castle.URL_PRIVACY + "users", payload);
}
@Override
- public CastleResponse put(String path, Object payload) {
+ public CastleResponse eventsSchema() {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.put(path, payload);
+ return restApi.get(Castle.URL_EVENTS + "/schema");
}
@Override
- public CastleResponse delete(String path) {
+ public CastleResponse queryEvents(ImmutableMap payload) {
+ Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.delete(path);
+ return restApi.post(Castle.URL_EVENTS + "/query", payload);
}
@Override
- public CastleResponse delete(String path, Object payload) {
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.delete(path, payload);
- }
-
- public CastleResponse risk(ImmutableMap payload) {
+ public CastleResponse groupEvents(ImmutableMap payload) {
Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.post(Castle.URL_RISK, payload);
+ return restApi.post(Castle.URL_EVENTS + "/group", payload);
}
@Override
@@ -482,55 +299,4 @@ public CastleResponse log(Log payload) {
return restApi.post(Castle.URL_LOG, payload);
}
- @Override
- public CastleResponse recover(String userId) {
- Preconditions.checkNotNull(userId, "UserId can not be null");
- Preconditions.checkArgument(!userId.isEmpty());
-
- RestApi restApi = configuration.getRestApiFactory().buildBackend();
- return restApi.put(String.format(Castle.URL_RECOVER, userId));
- }
-
- private CastleMessage buildMessage(String event, String userId, @Nullable Object properties, @Nullable Object traits) {
- CastleMessage message = new CastleMessage(event);
-
- message.setUserId(userId);
-
- return setTraitsAndProperties(message, properties, traits);
- }
-
- private CastleMessage setTraitsAndProperties(CastleMessage message, @Nullable Object properties, @Nullable Object traits) {
- if (properties != null) {
- JsonElement propertiesJson = configuration.getModel().getGson().toJsonTree(properties);
- message.setProperties(propertiesJson);
- }
-
- if (traits != null) {
- JsonElement traitsJson = configuration.getModel().getGson().toJsonTree(traits);
- message.setUserTraits(traitsJson);
- }
-
- return message;
- }
-
- private JsonElement buildJson(CastleMessage message) throws CastleRuntimeException {
- JsonObject contextJson;
- // Context can be either from the message or from the instance of this
- // class. Make sure we have one
- CastleContext context = message.getContext();
- if (context == null) {
- contextJson = this.contextJson;
- } else {
- contextJson = configuration.getModel().getGson().toJsonTree(context).getAsJsonObject();
- }
-
- JsonElement messageJson = configuration.getModel().getGson().toJsonTree(message);
- JsonObject messageObj = messageJson.getAsJsonObject();
- messageObj.add("context", contextJson);
-
- // Add sent_at to json
- messageObj.addProperty("sent_at", Timestamp.timestamp());
-
- return messageObj;
- }
}
diff --git a/src/main/java/io/castle/client/internal/backend/OkHttpFactory.java b/src/main/java/io/castle/client/internal/backend/OkHttpFactory.java
index 4b32028f..b63b7d7c 100644
--- a/src/main/java/io/castle/client/internal/backend/OkHttpFactory.java
+++ b/src/main/java/io/castle/client/internal/backend/OkHttpFactory.java
@@ -36,8 +36,8 @@ private OkHttpClient createOkHttpClient() {
.writeTimeout(configuration.getTimeout(), TimeUnit.MILLISECONDS);
if (configuration.isLogHttpRequests()) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
- // TODO provide more configurable logging features.
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
+ logging.redactHeader("Authorization");
builder = builder.addInterceptor(logging);
}
@@ -61,4 +61,10 @@ public Response intercept(Chain chain) throws IOException {
public RestApi buildBackend() {
return new OkRestApiBackend(client, modelInstance, configuration);
}
+
+ @Override
+ public void close() {
+ client.dispatcher().executorService().shutdown();
+ client.connectionPool().evictAll();
+ }
}
diff --git a/src/main/java/io/castle/client/internal/backend/OkRestApiBackend.java b/src/main/java/io/castle/client/internal/backend/OkRestApiBackend.java
index 46c77f6c..ad551005 100644
--- a/src/main/java/io/castle/client/internal/backend/OkRestApiBackend.java
+++ b/src/main/java/io/castle/client/internal/backend/OkRestApiBackend.java
@@ -1,12 +1,9 @@
package io.castle.client.internal.backend;
import com.google.gson.*;
-import io.castle.client.Castle;
import io.castle.client.internal.config.CastleConfiguration;
import io.castle.client.internal.json.CastleGsonModel;
import io.castle.client.internal.utils.OkHttpExceptionUtil;
-import io.castle.client.internal.utils.VerdictBuilder;
-import io.castle.client.internal.utils.VerdictTransportModel;
import io.castle.client.model.*;
import okhttp3.*;
@@ -25,224 +22,15 @@ public class OkRestApiBackend implements RestApi {
private final CastleConfiguration configuration;
private final HttpUrl baseUrl;
- private final HttpUrl track;
- private final HttpUrl authenticate;
- private final HttpUrl deviceBase;
- private final HttpUrl userBase;
- private final HttpUrl impersonateBase;
- private final HttpUrl privacyBase;
public OkRestApiBackend(OkHttpClient client, CastleGsonModel model, CastleConfiguration configuration) {
this.baseUrl = HttpUrl.parse(configuration.getApiBaseUrl());
this.client = client;
this.model = model;
this.configuration = configuration;
- this.track = baseUrl.resolve(Castle.URL_TRACK);
- this.authenticate = baseUrl.resolve(Castle.URL_AUTHENTICATE);
- this.deviceBase = baseUrl.resolve(Castle.URL_DEVICES);
- this.userBase = baseUrl.resolve(Castle.URL_USERS);
- this.impersonateBase = baseUrl.resolve(Castle.URL_IMPERSONATE);
- this.privacyBase = baseUrl.resolve(Castle.URL_PRIVACY);
}
@Override
- public void sendTrackRequest(JsonElement payload, final AsyncCallbackHandler asyncCallbackHandler) {
- RequestBody body = buildRequestBody(payload);
- Request request = new Request.Builder()
- .url(track)
- .post(body)
- .build();
- client.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- Castle.logger.error("HTTP layer. Error sending track request.", e);
- if (asyncCallbackHandler != null) {
- asyncCallbackHandler.onException(e);
- }
- }
-
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- try (ResponseBody responseBody = response.body()) {
- if (asyncCallbackHandler != null) {
- asyncCallbackHandler.onResponse(response.isSuccessful());
- }
- }
- }
- });
- }
-
- @Override
- public Verdict sendAuthenticateSync(JsonElement payloadJson) {
- final String userId = getUserIdFromPayload(payloadJson);
-
- RequestBody body = buildRequestBody(payloadJson);
- Request request = new Request.Builder()
- .url(authenticate)
- .post(body)
- .build();
- try (Response response = client.newCall(request).execute()) {
- return extractAuthenticationAction(response, userId);
- } catch (IOException e) {
- Castle.logger.error("HTTP layer. Error sending request.", e);
- if (configuration.getAuthenticateFailoverStrategy().isThrowTimeoutException()) {
- throw OkHttpExceptionUtil.handle(e);
- } else {
- return VerdictBuilder.failover(e.getMessage())
- .withAction(configuration.getAuthenticateFailoverStrategy().getDefaultAction())
- .withUserId(userId)
- .build();
- }
- }
- }
-
- @Override
- public void sendAuthenticateAsync(JsonElement payloadJson, final AsyncCallbackHandler asyncCallbackHandler) {
- final String userId = getUserIdFromPayload(payloadJson);
-
- RequestBody body = buildRequestBody(payloadJson);
- Request request = new Request.Builder()
- .url(authenticate)
- .post(body)
- .build();
- client.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- if (configuration.getAuthenticateFailoverStrategy().isThrowTimeoutException()) {
- asyncCallbackHandler.onException(OkHttpExceptionUtil.handle(e));
- } else {
- asyncCallbackHandler.onResponse(
- VerdictBuilder.failover(e.getMessage())
- .withAction(configuration.getAuthenticateFailoverStrategy().getDefaultAction())
- .withUserId(userId)
- .build()
- );
- }
- }
-
- @Override
- public void onResponse(Call call, Response response) throws IOException {
- try (ResponseBody responseBody = response.body()) {
- asyncCallbackHandler.onResponse(extractAuthenticationAction(response, userId));
- }
- }
- });
- }
-
- private String getUserIdFromPayload(JsonElement payloadJson) {
- final String userId = ((JsonObject) payloadJson).has("user_id") ? ((JsonObject) payloadJson).get("user_id").getAsString() : null;
- if (userId == null) {
- Castle.logger.warn("Authenticate called with user_id null. Is this correct?");
- }
- return userId;
- }
-
- private RequestBody buildRequestBody(JsonElement payloadJson) {
- JsonObject json = payloadJson.getAsJsonObject();
- return RequestBody.create(json.toString(), JSON);
- }
-
- private Verdict extractAuthenticationAction(Response response, String userId) throws IOException {
- String errorReason = response.message();
- String jsonResponse = response.body().string();
-
- if (response.isSuccessful()) {
- Gson gson = model.getGson();
- VerdictTransportModel transport = gson.fromJson(jsonResponse, VerdictTransportModel.class);
- if (transport != null && transport.getAction() != null) {
- return VerdictBuilder.fromTransport(transport, JsonParser.parseString(jsonResponse));
- } else {
- errorReason = "Invalid JSON in response";
- }
- }
-
- if (response.code() >= 500) {
- //Use failover for error backends calls.
- if (!configuration.getAuthenticateFailoverStrategy().isThrowTimeoutException()) {
- Verdict verdict = VerdictBuilder.failover(errorReason)
- .withAction(configuration.getAuthenticateFailoverStrategy().getDefaultAction())
- .withUserId(userId)
- .build();
- return verdict;
- } else {
- throw new CastleApiInternalServerErrorException(response);
- }
- }
-
- // Could not extract Verdict, so fail for client logic space.
- throw new CastleRuntimeException(response);
- }
-
- @Override
- public Boolean sendPrivacyRemoveUser(String userId) {
- Request request = createPrivacyRemoveRequest(userId);
- try (Response response = client.newCall(request).execute()) {
- return extractResponse(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
- @Override
- public CastleUserDevice sendApproveDeviceRequestSync(String deviceToken) {
- Request request = createApproveDeviceRequest(deviceToken);
- try (Response response = client.newCall(request).execute()) {
- return extractDevice(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
- @Override
- public CastleUserDevice sendReportDeviceRequestSync(String deviceToken) {
- Request request = createReportDeviceRequest(deviceToken);
- try (Response response = client.newCall(request).execute()) {
- return extractDevice(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
- @Override
- public CastleUserDevices sendGetUserDevicesRequestSync(String userId) {
- Request request = createGetUserDevicesRequest(userId);
- try (Response response = client.newCall(request).execute()) {
- return extractDevices(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
- @Override
- public CastleUserDevice sendGetUserDeviceRequestSync(String deviceToken) {
- Request request = createGetUserDeviceRequest(deviceToken);
- try (Response response = client.newCall(request).execute()) {
- return extractDevice(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
- @Override
- public CastleSuccess sendImpersonateStartRequestSync(String userId, String impersonator, JsonObject contextJson) {
- Request request = createImpersonateStartRequest(userId, impersonator, contextJson);
- try (Response response = client.newCall(request).execute()) {
- return extractSuccess(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
- @Override
- public CastleSuccess sendImpersonateEndRequestSync(String userId, String impersonator, JsonObject contextJson) {
- Request request = createImpersonateEndRequest(userId, impersonator, contextJson);
- try (Response response = client.newCall(request).execute()) {
- return extractSuccess(response);
- } catch (IOException e) {
- throw OkHttpExceptionUtil.handle(e);
- }
- }
-
public CastleResponse get(String path) {
return makeRequest(path, null, METHOD_GET);
}
@@ -302,118 +90,6 @@ private CastleResponse makeRequest(String path, JsonElement payload, String meth
}
}
- private CastleUserDevice extractDevice(Response response) throws IOException {
- return (CastleUserDevice) extract(response, CastleUserDevice.class);
- }
-
- private CastleUserDevices extractDevices(Response response) throws IOException {
- return (CastleUserDevices) extract(response, CastleUserDevices.class);
- }
-
- private Object extract(Response response, Class clazz) throws IOException {
- if (response.isSuccessful()) {
- String jsonResponse = response.body().string();
- Gson gson = model.getGson();
- return gson.fromJson(jsonResponse, clazz);
- } else if (response.code() == 404) {
- return null;
- }
- OkHttpExceptionUtil.handle(response);
- return null;
- }
-
- private CastleSuccess extractSuccess(Response response) throws IOException {
- if (response.isSuccessful()) {
- if (response.body() != null) {
- String jsonResponse = response.body().string();
- Gson gson = model.getGson();
- return gson.fromJson(jsonResponse, CastleSuccess.class);
- }
- }
- OkHttpExceptionUtil.handle(response);
- return null;
- }
-
- private Boolean extractResponse(Response response) {
- if (response.isSuccessful()) {
- return true;
- } else if (response.code() == 404) {
- return null;
- }
- OkHttpExceptionUtil.handle(response);
- return false;
- }
-
- private CastleUser extractUser(Response response) throws IOException {
- return (CastleUser) extract(response, CastleUser.class);
- }
-
- private Request createApproveDeviceRequest(String deviceToken) {
- HttpUrl approveDeviceUrl = deviceBase.resolve(deviceToken + "/approve");
- return new Request.Builder()
- .url(approveDeviceUrl)
- .put(createEmptyRequestBody())
- .build();
- }
-
- private Request createReportDeviceRequest(String deviceToken) {
- HttpUrl reportDeviceUrl = deviceBase.resolve(deviceToken + "/report");
- return new Request.Builder()
- .url(reportDeviceUrl)
- .put(createEmptyRequestBody())
- .build();
- }
-
- private Request createGetUserDevicesRequest(String userId) {
- HttpUrl getUserDevicesUrl = userBase.resolve(userId + "/devices");
- return new Request.Builder()
- .url(getUserDevicesUrl)
- .get()
- .build();
- }
-
- private Request createGetUserDeviceRequest(String deviceToken) {
- HttpUrl getUserDeviceUrl = deviceBase.resolve(deviceToken);
- return new Request.Builder()
- .url(getUserDeviceUrl)
- .get()
- .build();
- }
-
- private Request createImpersonateStartRequest(String userId, String impersonator, JsonObject contextJson) {
- HttpUrl impersonateUrl = impersonateBase;
-
- ImpersonatePayload payload = new ImpersonatePayload(userId, impersonator, contextJson);
-
- RequestBody body = RequestBody.create(model.getGson().toJson(payload), JSON);
-
- return new Request.Builder()
- .url(impersonateUrl)
- .post(body)
- .build();
- }
-
- private Request createImpersonateEndRequest(String userId, String impersonator, JsonObject contextJson) {
- HttpUrl impersonateUrl = impersonateBase;
-
- ImpersonatePayload payload = new ImpersonatePayload(userId, impersonator, contextJson);
-
- RequestBody body = RequestBody.create(model.getGson().toJson(payload), JSON);
-
- return new Request.Builder()
- .url(impersonateUrl)
- .delete(body)
- .build();
- }
-
- private Request createPrivacyRemoveRequest(String userId) {
- HttpUrl privacyRemoveUrl = privacyBase.resolve("users/" + userId);
- return new Request.Builder()
- .url(privacyRemoveUrl)
- .delete()
- .build();
- }
-
private RequestBody createEmptyRequestBody() {
return RequestBody.create(new byte[0], null);
}
diff --git a/src/main/java/io/castle/client/internal/backend/RestApi.java b/src/main/java/io/castle/client/internal/backend/RestApi.java
index 1c19d081..5663aca7 100644
--- a/src/main/java/io/castle/client/internal/backend/RestApi.java
+++ b/src/main/java/io/castle/client/internal/backend/RestApi.java
@@ -1,93 +1,11 @@
package io.castle.client.internal.backend;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
import io.castle.client.model.*;
public interface RestApi {
/**
- *
- * @param payloadJson JSON object containing the event properties
- * @param asyncCallbackHandler callback to inform if request was correctly sent
- */
- void sendTrackRequest(JsonElement payloadJson, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- *
- * @param payloadJson JSON object containing the event properties
- * @return Verdict to be used in login logic
- */
- Verdict sendAuthenticateSync(JsonElement payloadJson);
-
- /**
- *
- * @param payloadJson JSON object containing the event properties
- * @param asyncCallbackHandler callback to inform if request was correctly sent
- */
- void sendAuthenticateAsync(JsonElement payloadJson, AsyncCallbackHandler asyncCallbackHandler);
-
- /**
- * Remove user from Castle (GDPR reasons)
- * @see The docs
- * @param userId user id to be removed
- */
- Boolean sendPrivacyRemoveUser(String userId);
-
- /**
- * Sync call to the approve device endpoint.
- *
- * @param deviceToken string representing the token for the device to get
- * @return a {@code device} with metadata contained in the body of the response
- */
- CastleUserDevice sendApproveDeviceRequestSync(String deviceToken);
-
- /**
- * Sync call to the report device endpoint.
- *
- * @param deviceToken string representing the token for the device to get
- * @return a {@code device} with metadata contained in the body of the response
- */
- CastleUserDevice sendReportDeviceRequestSync(String deviceToken);
-
- /**
- * Sync call to the devices endpoint.
- *
- * @param userId string representing the user to get devices for
- * @return a {@code devices} with metadata contained in the body of the response
- */
- CastleUserDevices sendGetUserDevicesRequestSync(String userId);
-
- /**
- * Sync call to the device endpoint.
- *
- * @param deviceToken string representing the token for the device to get
- * @return a {@code device} with metadata contained in the body of the response
- */
- CastleUserDevice sendGetUserDeviceRequestSync(String deviceToken);
-
- /**
- * Sync call to the impersonate endpoint.
- *
- * @param userId id of the user to impersonate
- * @param impersonator id of the user doing the impersonation
- * @param contextJson context json
- * @return a success message
- */
- CastleSuccess sendImpersonateStartRequestSync(String userId, String impersonator, JsonObject contextJson);
-
- /**
- * Sync call to the impersonate endpoint.
- *
- * @param userId id of the user to stop impersonating
- * @param impersonator id of the user doing the impersonation
- * @param contextJson context json
- * @return a success message
- */
- CastleSuccess sendImpersonateEndRequestSync(String userId, String impersonator, JsonObject contextJson);
-
- /**
- * Make a GET request to a Castle API endpoint such as /v1/{userId}/devices
+ * Make a GET request to a Castle API endpoint such as /v1/lists
*
* @param path api path
* @return a decoded json response
@@ -95,7 +13,7 @@ public interface RestApi {
CastleResponse get(String path);
/**
- * Make a POST request to a Castle API endpoint such as /v1/track
+ * Make a POST request to a Castle API endpoint such as /v1/risk
*
* @param path api path
* @param payload request payload
@@ -104,7 +22,7 @@ public interface RestApi {
CastleResponse post(String path, Object payload);
/**
- * Make a PUT request to a Castle API endpoint such as /v1/devices/{deviceToken}/report
+ * Make a PUT request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @return a decoded json response
@@ -112,7 +30,7 @@ public interface RestApi {
CastleResponse put(String path);
/**
- * Make a PUT request to a Castle API endpoint such as /v1/devices/{deviceToken}/report
+ * Make a PUT request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @param payload request payload
@@ -121,7 +39,7 @@ public interface RestApi {
CastleResponse put(String path, Object payload);
/**
- * Make a DELETE request to a Castle API endpoint such as /v1/impersonate
+ * Make a DELETE request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @return a decoded json response
@@ -129,7 +47,7 @@ public interface RestApi {
CastleResponse delete(String path);
/**
- * Make a DELETE request to a Castle API endpoint such as /v1/impersonate
+ * Make a DELETE request to a Castle API endpoint such as /v1/lists/{id}
*
* @param path api path
* @param payload request payload
diff --git a/src/main/java/io/castle/client/internal/backend/RestApiFactory.java b/src/main/java/io/castle/client/internal/backend/RestApiFactory.java
index a758b005..e01b297b 100644
--- a/src/main/java/io/castle/client/internal/backend/RestApiFactory.java
+++ b/src/main/java/io/castle/client/internal/backend/RestApiFactory.java
@@ -3,4 +3,6 @@
public interface RestApiFactory {
RestApi buildBackend();
+
+ void close();
}
diff --git a/src/main/java/io/castle/client/internal/config/CastleConfiguration.java b/src/main/java/io/castle/client/internal/config/CastleConfiguration.java
index d4b5f73d..cd3952ea 100644
--- a/src/main/java/io/castle/client/internal/config/CastleConfiguration.java
+++ b/src/main/java/io/castle/client/internal/config/CastleConfiguration.java
@@ -1,7 +1,6 @@
package io.castle.client.internal.config;
import io.castle.client.internal.backend.CastleBackendProvider;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleRuntimeException;
import java.util.List;
@@ -20,15 +19,10 @@ public class CastleConfiguration {
private final String apiBaseUrl;
/**
- * Timeout after which a request fails.
+ * Timeout in milliseconds applied to connect, read, and write.
*/
private final int timeout;
- /**
- * Strategy for returning a {@code verdict} when an authenticate call fails.
- */
- private final AuthenticateFailoverStrategy authenticateFailoverStrategy;
-
/**
* List of headers that will get passed to the {@code CastleContext} unless they are denyListed.
*/
@@ -66,10 +60,9 @@ public class CastleConfiguration {
*/
private final int maxRequests;
- public CastleConfiguration(String apiBaseUrl, int timeout, AuthenticateFailoverStrategy authenticateFailoverStrategy, List allowListHeaders, List denyListHeaders, String apiSecret, String castleAppId, CastleBackendProvider backendProvider, boolean logHttpRequests, List ipHeaders, Integer maxRequests) {
+ public CastleConfiguration(String apiBaseUrl, int timeout, List allowListHeaders, List denyListHeaders, String apiSecret, String castleAppId, CastleBackendProvider backendProvider, boolean logHttpRequests, List ipHeaders, Integer maxRequests) {
this.apiBaseUrl = apiBaseUrl;
this.timeout = timeout;
- this.authenticateFailoverStrategy = authenticateFailoverStrategy;
this.allowListHeaders = allowListHeaders;
this.denyListHeaders = denyListHeaders;
this.apiSecret = apiSecret;
@@ -88,10 +81,6 @@ public int getTimeout() {
return timeout;
}
- public AuthenticateFailoverStrategy getAuthenticateFailoverStrategy() {
- return authenticateFailoverStrategy;
- }
-
public List getAllowListHeaders() {
return allowListHeaders;
}
diff --git a/src/main/java/io/castle/client/internal/config/CastleConfigurationBuilder.java b/src/main/java/io/castle/client/internal/config/CastleConfigurationBuilder.java
index ab177de1..c738f9fd 100644
--- a/src/main/java/io/castle/client/internal/config/CastleConfigurationBuilder.java
+++ b/src/main/java/io/castle/client/internal/config/CastleConfigurationBuilder.java
@@ -4,10 +4,9 @@
import com.google.common.collect.ImmutableList;
import io.castle.client.internal.backend.CastleBackendProvider;
import io.castle.client.internal.utils.HeaderNormalizer;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleSdkConfigurationException;
+import java.time.Duration;
import java.util.LinkedList;
import java.util.List;
@@ -19,7 +18,6 @@
* The fields that can be set in a CastleConfiguration:
*
* timeout
- * failoverStrategy
* allowListHeaders
* denyListHeaders
* apiSecret
@@ -38,14 +36,9 @@
*/
public class CastleConfigurationBuilder {
/**
- * Represents the milliseconds after which a request fails.
+ * Milliseconds applied to connect, read, and write for each request.
*/
- private int timeout = 500;
-
- /**
- * Strategy used when an authenticate call to the Castle API fails.
- */
- private AuthenticateFailoverStrategy failoverStrategy;
+ private int timeout = 1000;
/**
* Strings representing headers that should be passed to the context object unless they are also denyListed.
@@ -110,8 +103,7 @@ public static CastleConfigurationBuilder defaultConfigBuilder() {
.withDefaultAllowList()
.withDefaultDenyList()
.withDefaultApiBaseUrl()
- .withTimeout(500)
- .withDefaultAuthenticateFailoverStrategy()
+ .withTimeout(1000)
.withDefaultBackendProvider()
.withMaxRequests(5);
return builder;
@@ -120,7 +112,7 @@ public static CastleConfigurationBuilder defaultConfigBuilder() {
/**
* Provides a fresh castleConfigurationBuilder.
*
- * The only default value provided is the timeout, which is set to 500 milliseconds.
+ * The only default value provided is the timeout, which is set to 1000 milliseconds.
*
* @return a castleConfigurationBuilder with all values set to null, except timeout
*/
@@ -141,7 +133,7 @@ public CastleConfigurationBuilder withDefaultAllowList() {
/**
* Sets the default list of denyListed headers.
*
- * The default value is a list whose single element is the {@code Cookie} header.
+ * The default value is {@code Cookie} and {@code Authorization}.
*
* @return a castleConfigurationBuilder instance with the default list of denyListed headers
*/
@@ -153,7 +145,7 @@ public CastleConfigurationBuilder withDefaultDenyList() {
}
/**
- * Sets the timeout in milliseconds for a request.
+ * Sets the connect, read, and write timeout in milliseconds for a request.
*
* @param timeout milliseconds after which a request times out
* @return a castleConfigurationBuilder with a timeout set to a new value
@@ -164,26 +156,13 @@ public CastleConfigurationBuilder withTimeout(int timeout) {
}
/**
- * Establishes the authentication strategy that will be used in case of a timeout when performing a
- * {@code CastleApi#authenticate} call.
- *
- * @param failoverStrategy strategy to use for failed authenticate API calls; not null.
- * @return a castleConfigurationBuilder with the chosen AuthenticationStrategy set
- */
- public CastleConfigurationBuilder withAuthenticateFailoverStrategy(AuthenticateFailoverStrategy failoverStrategy) {
- this.failoverStrategy = failoverStrategy;
- return this;
- }
-
- /**
- * Sets the failover strategy for the authenticate Castle API call to allow.
- *
- * The authenticate failover strategy for the default configuration is to return {@link AuthenticateAction#ALLOW}.
+ * Sets the connect, read, and write timeout for a request.
*
- * @return a castleConfigurationBuilder with allow as the authenticate failover strategy
+ * @param timeout duration after which a request times out
+ * @return a castleConfigurationBuilder with a timeout set to a new value
*/
- public CastleConfigurationBuilder withDefaultAuthenticateFailoverStrategy() {
- return this.withAuthenticateFailoverStrategy(new AuthenticateFailoverStrategy(AuthenticateAction.ALLOW));
+ public CastleConfigurationBuilder withTimeout(Duration timeout) {
+ return withTimeout(Math.toIntExact(timeout.toMillis()));
}
/**
@@ -292,7 +271,7 @@ public CastleConfigurationBuilder apiSecret(String apiSecret) {
*
* @return a castleConfiguration with all fields set to some meaningful value
* @throws CastleSdkConfigurationException if at least one of castleAppId, apiSecret, allowListHeaders,
- * denyListHeaders, failoverStrategy, backendProvider is not provided
+ * denyListHeaders, backendProvider is not provided
* during the building stage of the
* CastleConfiguration instance.
*/
@@ -310,10 +289,6 @@ public CastleConfiguration build() throws CastleSdkConfigurationException {
builder.add("A denyList of headers must be provided. If not sure, then use the default values provided " +
"by method withDefaultDenyList. Read documentation for further details.");
}
- if (failoverStrategy == null) {
- builder.add("A failover strategy must be provided. If not sure, then use the default values provided " +
- "by method withDefaultAuthenticateFailoverStrategy. Read documentation for further details.");
- }
if (backendProvider == null) {
builder.add("A backend provider must be selected. If not sure, then use the default values provided " +
"by method withDefaultBackendProvider. Read documentation for further details.");
@@ -328,7 +303,6 @@ public CastleConfiguration build() throws CastleSdkConfigurationException {
HeaderNormalizer normalizer = new HeaderNormalizer();
return new CastleConfiguration(apiBaseUrl,
timeout,
- failoverStrategy,
normalizer.normalizeList(allowListHeaders),
normalizer.normalizeList(denyListHeaders),
apiSecret,
@@ -378,6 +352,7 @@ public CastleConfigurationBuilder withApiBaseUrl(String apiBaseUrl) {
/**
* Flag to enable logging on backend HTTP requests.
+ * When enabled, the {@code Authorization} header is redacted from the log.
*
* @param logHttpRequests boolean to switch logging on or off.
* @return a castleConfigurationBuilder with logging setup set
diff --git a/src/main/java/io/castle/client/internal/config/CastleSdkInternalConfiguration.java b/src/main/java/io/castle/client/internal/config/CastleSdkInternalConfiguration.java
index 4756fd7c..58e01899 100644
--- a/src/main/java/io/castle/client/internal/config/CastleSdkInternalConfiguration.java
+++ b/src/main/java/io/castle/client/internal/config/CastleSdkInternalConfiguration.java
@@ -70,4 +70,8 @@ public CastleConfiguration getConfiguration() {
public HashFunction getSecureHashFunction() {
return Hashing.hmacSha256(sha256Key);
}
+
+ public void close() {
+ restApiFactory.close();
+ }
}
diff --git a/src/main/java/io/castle/client/internal/config/ConfigurationLoader.java b/src/main/java/io/castle/client/internal/config/ConfigurationLoader.java
index 30513ab0..598e769f 100644
--- a/src/main/java/io/castle/client/internal/config/ConfigurationLoader.java
+++ b/src/main/java/io/castle/client/internal/config/ConfigurationLoader.java
@@ -3,8 +3,6 @@
import com.google.common.base.Splitter;
import io.castle.client.Castle;
import io.castle.client.internal.backend.CastleBackendProvider;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleSdkConfigurationException;
import java.io.InputStream;
@@ -78,8 +76,6 @@ private static String getPropertiesFilePath() {
* Loads the application level configuration for the Castle SDK with values taken from environmental variables,
* a properties file or default values in that order of precedence.
*
- * When wrong failover strategy or backend, null is returned instead.
- *
* @return a CastleConfiguration instance
* @throws CastleSdkConfigurationException if at least one of apiSecret or castleAppId is not provided in either the
* environment or the properties file in the classpath
@@ -130,11 +126,6 @@ public CastleConfigurationBuilder loadConfigurationBuilder() {
"backend_provider",
"CASTLE_SDK_BACKEND_PROVIDER"
);
- String authenticateFailoverStrategyValue = loadConfigurationValue(
- castleConfigurationProperties,
- "failover_strategy",
- "CASTLE_SDK_AUTHENTICATE_FAILOVER_STRATEGY"
- );
String apiBaseUrl = loadConfigurationValue(
castleConfigurationProperties,
"base_url",
@@ -170,19 +161,6 @@ public CastleConfigurationBuilder loadConfigurationBuilder() {
int timeout = Integer.parseInt(timeoutValue);
builder.withTimeout(timeout);
}
- if (authenticateFailoverStrategyValue != null) {
- if (authenticateFailoverStrategyValue.compareTo("throw") == 0) {
- builder.withAuthenticateFailoverStrategy(new AuthenticateFailoverStrategy());
- } else {
- builder.withAuthenticateFailoverStrategy(
- new AuthenticateFailoverStrategy(
- AuthenticateAction.fromAction(authenticateFailoverStrategyValue)
- )
- );
- }
- } else {
- builder.withDefaultAuthenticateFailoverStrategy();
- }
if (backendProviderValue != null) {
builder.withBackendProvider(
CastleBackendProvider.valueOf(backendProviderValue)
diff --git a/src/main/java/io/castle/client/internal/json/AuthenticateActionDeserializer.java b/src/main/java/io/castle/client/internal/json/AuthenticateActionDeserializer.java
deleted file mode 100644
index 05837ac4..00000000
--- a/src/main/java/io/castle/client/internal/json/AuthenticateActionDeserializer.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.castle.client.internal.json;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import io.castle.client.model.AuthenticateAction;
-
-import java.lang.reflect.Type;
-
-public class AuthenticateActionDeserializer implements JsonDeserializer {
- @Override
- public AuthenticateAction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
- return AuthenticateAction.fromAction(json.getAsString());
- }
-}
diff --git a/src/main/java/io/castle/client/internal/json/CastleGsonModel.java b/src/main/java/io/castle/client/internal/json/CastleGsonModel.java
index ff0b65a0..b25a5967 100644
--- a/src/main/java/io/castle/client/internal/json/CastleGsonModel.java
+++ b/src/main/java/io/castle/client/internal/json/CastleGsonModel.java
@@ -4,10 +4,8 @@
import com.google.gson.internal.bind.util.ISO8601Utils;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
-import io.castle.client.model.AuthenticateAction;
import io.castle.client.model.RiskPolicyType;
import io.castle.client.model.CastleHeaders;
-import io.castle.client.model.CastleMessage;
import io.castle.client.model.generated.BaseChangesetEntry;
import okio.ByteString;
import org.threeten.bp.LocalDate;
@@ -27,9 +25,7 @@ public class CastleGsonModel {
public CastleGsonModel() {
GsonBuilder builder = createGsonBuilder();
builder.registerTypeAdapter(CastleHeaders.class, new CastleHeadersSerializer());
- builder.registerTypeAdapter(CastleMessage.class, new CastleMessageSerializer());
builder.registerTypeAdapter(CastleHeaders.class, new CastleHeadersDeserializer());
- builder.registerTypeAdapter(AuthenticateAction.class, new AuthenticateActionDeserializer());
builder.registerTypeAdapter(RiskPolicyType.class, new RiskPolicyTypeDeserializer());
builder.registerTypeAdapterFactory(ChangesetEntryTypeAdapter.FACTORY);
builder.registerTypeAdapter(BaseChangesetEntry.class, new BaseChangesetEntryDeserializer());
diff --git a/src/main/java/io/castle/client/internal/json/CastleMessageSerializer.java b/src/main/java/io/castle/client/internal/json/CastleMessageSerializer.java
deleted file mode 100644
index 51d819b3..00000000
--- a/src/main/java/io/castle/client/internal/json/CastleMessageSerializer.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.castle.client.internal.json;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-import com.google.gson.Gson;
-
-import io.castle.client.internal.utils.ContextMerge;
-import io.castle.client.model.CastleMessage;
-
-import java.lang.reflect.Type;
-import java.util.HashMap;
-
-public class CastleMessageSerializer implements JsonSerializer {
-
- private final Gson gson = CastleGsonModel.createGsonBuilder().create();
-
- @Override
- public JsonElement serialize(CastleMessage message, Type typeOfSrc, JsonSerializationContext context) {
- JsonElement root = gson.toJsonTree(message);
- HashMap other = message.getOther();
- if (other == null) {
- return root;
- }
- JsonElement otherJson = context.serialize(message.getOther());
- ContextMerge merger = new ContextMerge();
- return merger.merge(root.getAsJsonObject(), otherJson.getAsJsonObject());
- }
-}
diff --git a/src/main/java/io/castle/client/internal/utils/OkHttpExceptionUtil.java b/src/main/java/io/castle/client/internal/utils/OkHttpExceptionUtil.java
index 730002c8..d72a0ee9 100644
--- a/src/main/java/io/castle/client/internal/utils/OkHttpExceptionUtil.java
+++ b/src/main/java/io/castle/client/internal/utils/OkHttpExceptionUtil.java
@@ -34,6 +34,8 @@ public static void handle(Response response) throws CastleServerErrorException {
throw new CastleApiInvalidParametersException(response);
case 404:
throw new CastleApiNotFoundException(response);
+ case 402:
+ throw new CastleApiPaymentRequiredException(response);
}
throw new CastleServerErrorException(response);
}
diff --git a/src/main/java/io/castle/client/internal/utils/VerdictBuilder.java b/src/main/java/io/castle/client/internal/utils/VerdictBuilder.java
deleted file mode 100644
index df775030..00000000
--- a/src/main/java/io/castle/client/internal/utils/VerdictBuilder.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package io.castle.client.internal.utils;
-
-import com.google.gson.JsonElement;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.RiskPolicyResult;
-import io.castle.client.model.Verdict;
-
-public class VerdictBuilder {
- private AuthenticateAction action;
- private RiskPolicyResult riskPolicy;
- private String userId;
- private boolean failover;
- private String failoverReason;
- private String deviceToken;
- private JsonElement internal;
- private float risk;
-
- private VerdictBuilder() {
- }
-
- public static VerdictBuilder success() {
- return new VerdictBuilder()
- .withFailover(false);
- }
-
- public static VerdictBuilder failover(String failoverReason) {
- return new VerdictBuilder()
- .withFailover(true)
- .withFailoverReason(failoverReason);
- }
-
- public VerdictBuilder withAction(AuthenticateAction action) {
- this.action = action;
- return this;
- }
-
- public VerdictBuilder withUserId(String userId) {
- this.userId = userId;
- return this;
- }
-
-
- public VerdictBuilder withRiskPolicy(RiskPolicyResult riskPolicy) {
- this.riskPolicy = riskPolicy;
- return this;
- }
-
- public VerdictBuilder withDeviceToken(final String deviceToken) {
- this.deviceToken = deviceToken;
- return this;
- }
-
- public VerdictBuilder withRisk(final float risk) {
- this.risk = risk;
- return this;
- }
-
- public Verdict build() {
- Verdict verdict = new Verdict();
- verdict.setAction(action);
- verdict.setUserId(userId);
- verdict.setFailover(failover);
- verdict.setFailoverReason(failoverReason);
- verdict.setDeviceToken(deviceToken);
- verdict.setRiskPolicy(riskPolicy);
- verdict.setInternal(internal);
- verdict.setRisk(risk);
- return verdict;
- }
-
- public VerdictBuilder withFailover(boolean failover) {
- this.failover = failover;
- return this;
- }
-
- public VerdictBuilder withFailoverReason(String failoverReason) {
- this.failoverReason = failoverReason;
- return this;
- }
-
- public VerdictBuilder withInternal(JsonElement internal) {
- this.internal = internal;
- return this;
- }
-
- public static Verdict fromTransport(VerdictTransportModel transport, JsonElement internal) {
- internal.getAsJsonObject().get("action").getAsString();
- return success()
- .withAction(transport.getAction())
- .withUserId(transport.getUserId())
- .withDeviceToken(transport.getDeviceToken())
- .withRiskPolicy(transport.getRiskPolicy())
- .withRisk(transport.getRisk())
- .withInternal(internal)
- .build();
- }
-}
diff --git a/src/main/java/io/castle/client/internal/utils/VerdictTransportModel.java b/src/main/java/io/castle/client/internal/utils/VerdictTransportModel.java
deleted file mode 100644
index 07dab519..00000000
--- a/src/main/java/io/castle/client/internal/utils/VerdictTransportModel.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package io.castle.client.internal.utils;
-
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.RiskPolicyResult;
-
-public class VerdictTransportModel {
-
- private AuthenticateAction action;
- private RiskPolicyResult riskPolicy;
- private String userId;
- private String deviceToken;
- private float risk;
-
- public AuthenticateAction getAction() {
- return action;
- }
-
- public void setAction(AuthenticateAction action) {
- this.action = action;
- }
-
- public RiskPolicyResult getRiskPolicy() {
- return riskPolicy;
- }
-
- public void setRiskPolicy(RiskPolicyResult riskPolicy) {
- this.riskPolicy = riskPolicy;
- }
-
- public String getUserId() {
- return userId;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public String getDeviceToken() {
- return deviceToken;
- }
-
- public float getRisk() {
- return risk;
- }
-
- public void setRisk(float risk) {
- this.risk = risk;
- }
-
-}
diff --git a/src/main/java/io/castle/client/internal/utils/Webhook.java b/src/main/java/io/castle/client/internal/utils/Webhook.java
new file mode 100644
index 00000000..9c6ebf3c
--- /dev/null
+++ b/src/main/java/io/castle/client/internal/utils/Webhook.java
@@ -0,0 +1,56 @@
+package io.castle.client.internal.utils;
+
+import com.google.common.base.Charsets;
+import com.google.common.hash.Hashing;
+import com.google.common.io.BaseEncoding;
+
+import java.security.MessageDigest;
+
+/**
+ * Helper for verifying Castle webhook signatures.
+ *
+ * Castle signs every webhook with an HMAC-SHA256 of the raw request body using the
+ * account API secret. The signature is base64 encoded and delivered in the
+ * {@code X-Castle-Signature} header.
+ */
+public final class Webhook {
+
+ private Webhook() {
+ }
+
+ /**
+ * Computes the base64 encoded HMAC-SHA256 signature for the given body.
+ *
+ * @param secret the account API secret
+ * @param body the raw request body bytes
+ * @return the base64 encoded signature
+ */
+ public static String computeSignature(String secret, byte[] body) {
+ byte[] safeBody = body != null ? body : new byte[0];
+ byte[] hmac = Hashing.hmacSha256(secret.getBytes(Charsets.UTF_8))
+ .hashBytes(safeBody)
+ .asBytes();
+ return BaseEncoding.base64().encode(hmac);
+ }
+
+ /**
+ * Verifies that the supplied signature matches the computed signature for the body.
+ *
+ * The comparison is done in constant time to avoid timing attacks.
+ *
+ * @param secret the account API secret
+ * @param body the raw request body bytes
+ * @param signature the signature received in the {@code X-Castle-Signature} header
+ * @return {@code true} when the signature matches
+ */
+ public static boolean verifySignature(String secret, byte[] body, String signature) {
+ if (secret == null || signature == null) {
+ return false;
+ }
+ String expected = computeSignature(secret, body);
+ return MessageDigest.isEqual(
+ expected.getBytes(Charsets.UTF_8),
+ signature.getBytes(Charsets.UTF_8)
+ );
+ }
+}
diff --git a/src/main/java/io/castle/client/model/AsyncCallbackHandler.java b/src/main/java/io/castle/client/model/AsyncCallbackHandler.java
deleted file mode 100644
index 0286db15..00000000
--- a/src/main/java/io/castle/client/model/AsyncCallbackHandler.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.castle.client.model;
-
-/**
- * Callback interface used to handle async requests responses.
- *
- * An implementation of this interface allows the user to handle async request success and failure cases in a custom manner.
- *
- * @param The type of the internal response after execution.
- */
-public interface AsyncCallbackHandler {
- void onResponse(T response);
-
- void onException(Exception exception);
-}
diff --git a/src/main/java/io/castle/client/model/AuthenticateAction.java b/src/main/java/io/castle/client/model/AuthenticateAction.java
deleted file mode 100644
index 7839c701..00000000
--- a/src/main/java/io/castle/client/model/AuthenticateAction.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package io.castle.client.model;
-
-/**
- * Action that needs to be taken after a login attempt.
- *
- * See the documentation for the semantics of each case.
- * It can be null.
- *
- * @see Adaptive authentication
- */
-public enum AuthenticateAction {
- ALLOW, DENY, CHALLENGE;
-
- /**
- * Returns an AuthenticateAction from a string representing its name.
- *
- * @param action string representing the name of the AuthenticateAction, case-insensitive
- * @return the enum value matching the name, or null if it does not match any enum
- */
- public static AuthenticateAction fromAction(String action) {
- for (AuthenticateAction kind : AuthenticateAction.class.getEnumConstants()) {
- if (kind.name().equalsIgnoreCase(action)) {
- return kind;
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/io/castle/client/model/AuthenticateFailoverStrategy.java b/src/main/java/io/castle/client/model/AuthenticateFailoverStrategy.java
deleted file mode 100644
index 294b4600..00000000
--- a/src/main/java/io/castle/client/model/AuthenticateFailoverStrategy.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package io.castle.client.model;
-
-import io.castle.client.api.CastleApi;
-
-/**
- * Stores settings for either using an AuthenticationAction or throwing a TimeoutException,
- * if a Castle API authenticate call fails.
- *
- * If a {@link CastleApi#authenticate} call fails during an authentication attempt, the failover strategy specifies
- * which {@link AuthenticateAction} should be used.
- * Alternatively, a TimeoutException instance could be configured.
- */
-public class AuthenticateFailoverStrategy {
-
- private final AuthenticateAction defaultAction;
- private final boolean throwTimeoutException;
-
- private AuthenticateFailoverStrategy(AuthenticateAction defaultAction, boolean throwTimeoutException) {
- this.defaultAction = defaultAction;
- this.throwTimeoutException = throwTimeoutException;
- }
-
- /**
- * Sets the default authenticationAction for failed requests.
- *
- * @param action the authenticateAction that will be used as a default for failed requests
- */
- public AuthenticateFailoverStrategy(AuthenticateAction action) {
- this(action, false);
- }
-
- /**
- * Creates an authenticationStrategy whose policy is to throw a timeoutException.
- */
- public AuthenticateFailoverStrategy() {
- this(null, true);
- }
-
-
- /**
- * Gets the authenticateAction that this strategy uses for failover.
- *
- * @return the authenticateAction configured as default; null if there is no default AuthenticateAction
- */
- public AuthenticateAction getDefaultAction() {
- return defaultAction;
- }
-
- /**
- * Checks whether the failover strategy is configured to throw a timeoutException.
- *
- * @return true if the failover strategy is set to throw a timeoutException, false otherwise
- */
- public boolean isThrowTimeoutException() {
- return throwTimeoutException;
- }
-}
diff --git a/src/main/java/io/castle/client/model/CastleApiPaymentRequiredException.java b/src/main/java/io/castle/client/model/CastleApiPaymentRequiredException.java
new file mode 100644
index 00000000..9ef6258d
--- /dev/null
+++ b/src/main/java/io/castle/client/model/CastleApiPaymentRequiredException.java
@@ -0,0 +1,9 @@
+package io.castle.client.model;
+
+import okhttp3.Response;
+
+public class CastleApiPaymentRequiredException extends CastleServerErrorException {
+ public CastleApiPaymentRequiredException(Response response) {
+ super(response);
+ }
+}
diff --git a/src/main/java/io/castle/client/model/CastleMessage.java b/src/main/java/io/castle/client/model/CastleMessage.java
deleted file mode 100644
index ec7ba32d..00000000
--- a/src/main/java/io/castle/client/model/CastleMessage.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package io.castle.client.model;
-
-import java.util.HashMap;
-
-import javax.annotation.Nonnull;
-
-public class CastleMessage {
- private transient CastleContext context;
- private String createdAt;
- private String timestamp;
- private String deviceToken;
- @Nonnull private String event;
- /**
- * Collect other attributes that haven't not explicit setters to accommodate for
- * future parameters
- */
- private transient HashMap other;
- private Object properties;
- private String reviewId;
- private String userId;
- private Object userTraits;
-
- /**
- * Initialize a new request payload message
- * @param event Name of the event to send
- */
- public CastleMessage(String event) {
- setEvent(event);
- }
-
- public CastleContext getContext() {
- return context;
- }
-
- public void setContext(CastleContext context) {
- this.context = context;
- }
-
- /**
- * @deprecated use {@link #getTimestamp()} instead.
- */
- @Deprecated
- public String getCreatedAt() {
- return createdAt;
- }
-
- /**
- * @deprecated use {@link #setTimestamp(String)} instead.
- */
- @Deprecated
- public void setCreatedAt(String createdAt) {
- this.createdAt = createdAt;
- }
-
- public String getTimestamp() {
- return timestamp;
- }
-
- public void setTimestamp(String timestamp) {
- this.timestamp = timestamp;
- }
-
- public String getDeviceToken() {
- return deviceToken;
- }
-
- public void setDeviceToken(String deviceToken) {
- this.deviceToken = deviceToken;
- }
-
- @Nonnull
- public String getEvent() {
- return event;
- }
-
- public void setEvent(@Nonnull String event) {
- this.event = event;
- }
-
- public HashMap getOther() {
- if (other == null) {
- this.other = new HashMap();
- }
- return other;
- }
-
- public void setOther(HashMap other) {
- this.other = other;
- }
-
- public Object getProperties() {
- return properties;
- }
-
- public void setProperties(Object properties) {
- this.properties = properties;
- }
-
- public String getReviewId() {
- return reviewId;
- }
-
- public void setReviewId(String reviewId) {
- this.reviewId = reviewId;
- }
-
- public String getUserId() {
- return userId;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public Object getUserTraits() {
- return userTraits;
- }
-
- public void setUserTraits(Object userTraits) {
- this.userTraits = userTraits;
- }
-
- public static Builder builder(String event) {
- return new Builder(new CastleMessage(event));
- }
-
- public static class Builder {
- private CastleMessage payload;
-
- public Builder(CastleMessage payload) {
- this.payload = payload;
- }
-
- public CastleMessage build() {
- return payload;
- }
-
- public Builder context(CastleContext context) {
- payload.setContext(context);
- return this;
- }
-
- /**
- * @deprecated use {@link #timestamp(String)} instead.
- */
- @Deprecated
- public Builder createdAt(String createdAt) {
- payload.setCreatedAt(createdAt);
- return this;
- }
-
- public Builder timestamp(String timestamp) {
- payload.setTimestamp(timestamp);
- return this;
- }
-
- public Builder deviceToken(String deviceToken) {
- payload.setDeviceToken(deviceToken);
- return this;
- }
-
- public Builder reviewId(String reviewId) {
- payload.setReviewId(reviewId);
- return this;
- }
-
- public Builder properties(Object properties) {
- if (properties == null) {
- throw new NullPointerException("Null properties");
- }
- payload.setProperties(properties);
- return this;
- }
-
- public Builder other(HashMap other) {
- payload.setOther(other);
- return this;
- }
-
- public Builder put(String key, Object value) {
- payload.getOther().put(key, value);
- return this;
- }
-
- public Builder userId(String userId) {
- payload.setUserId(userId);
- return this;
- }
-
- public Builder userTraits(Object userTraits) {
- if (userTraits == null) {
- throw new NullPointerException("Null userTraits");
- }
- payload.setUserTraits(userTraits);
- return this;
- }
- }
-}
diff --git a/src/main/java/io/castle/client/model/CastleResponse.java b/src/main/java/io/castle/client/model/CastleResponse.java
index 2d2b3794..150bdc17 100644
--- a/src/main/java/io/castle/client/model/CastleResponse.java
+++ b/src/main/java/io/castle/client/model/CastleResponse.java
@@ -1,12 +1,18 @@
package io.castle.client.model;
import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
import com.google.gson.JsonParser;
import io.castle.client.internal.utils.OkHttpExceptionUtil;
import okhttp3.Response;
+import okhttp3.ResponseBody;
import java.io.IOException;
+/**
+ * Parsed HTTP response from the Castle API.
+ * An empty or missing body is represented as JSON null.
+ */
public class CastleResponse {
private final int code;
private final JsonElement json;
@@ -15,8 +21,9 @@ public CastleResponse(Response response) throws IOException {
OkHttpExceptionUtil.handle(response);
code = response.code();
- String body = response.body().string();
- json = JsonParser.parseString(body);
+ ResponseBody responseBody = response.body();
+ String body = responseBody != null ? responseBody.string() : "";
+ json = body.isBlank() ? JsonNull.INSTANCE : JsonParser.parseString(body);
}
public boolean isSuccessful() {
diff --git a/src/main/java/io/castle/client/model/CastleRuntimeException.java b/src/main/java/io/castle/client/model/CastleRuntimeException.java
index 226fc588..9330dd56 100644
--- a/src/main/java/io/castle/client/model/CastleRuntimeException.java
+++ b/src/main/java/io/castle/client/model/CastleRuntimeException.java
@@ -3,8 +3,7 @@
import okhttp3.Response;
/**
- * Exception thrown if the client is configured to use a THROW
- * {@code AuthenticateFailoverStrategy} and if a call to the authenticate endpoint of the Castle API fails.
+ * Unchecked exception thrown when a Castle API call fails.
*/
public class CastleRuntimeException extends RuntimeException {
diff --git a/src/main/java/io/castle/client/model/CastleSuccess.java b/src/main/java/io/castle/client/model/CastleSuccess.java
deleted file mode 100644
index 060999d0..00000000
--- a/src/main/java/io/castle/client/model/CastleSuccess.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.castle.client.model;
-
-public class CastleSuccess {
- boolean success;
-
- public boolean isSuccess() {
- return success;
- }
-
- public void setSuccess(boolean success) {
- this.success = success;
- }
-}
diff --git a/src/main/java/io/castle/client/model/CastleUserDevice.java b/src/main/java/io/castle/client/model/CastleUserDevice.java
deleted file mode 100644
index 6e751272..00000000
--- a/src/main/java/io/castle/client/model/CastleUserDevice.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package io.castle.client.model;
-
-/**
- * Model of the device object returned in the body of the response of a device call to the Castle Api.
- */
-public class CastleUserDevice {
- private String token;
- private double risk;
- private String createdAt;
- private String lastSeenAt;
- private String approvedAt;
- private String escalatedAt;
- private String mitigatedAt;
- private CastleUserDeviceContext context;
-
- boolean isCurrentDevice;
-
- public String getToken() {
- return token;
- }
-
- public void setToken(String token) {
- this.token = token;
- }
-
- public double getRisk() {
- return risk;
- }
-
- public void setRisk(double risk) {
- this.risk = risk;
- }
-
- public String getCreatedAt() {
- return createdAt;
- }
-
- public void setCreatedAt(String createdAt) {
- this.createdAt = createdAt;
- }
-
- public String getLastSeenAt() {
- return lastSeenAt;
- }
-
- public void setLastSeenAt(String lastSeenAt) {
- this.lastSeenAt = lastSeenAt;
- }
-
- public String getApprovedAt() {
- return approvedAt;
- }
-
- public void setApprovedAt(String approvedAt) {
- this.approvedAt = approvedAt;
- }
-
- public String getEscalatedAt() {
- return escalatedAt;
- }
-
- public void setEscalatedAt(String escalatedAt) {
- this.escalatedAt = escalatedAt;
- }
-
- public String getMitigatedAt() {
- return mitigatedAt;
- }
-
- public void setMitigatedAt(String mitigatedAt) {
- this.mitigatedAt = mitigatedAt;
- }
-
- public CastleUserDeviceContext getContext() {
- return context;
- }
-
- public void setContext(CastleUserDeviceContext context) {
- this.context = context;
- }
-
- public boolean isCurrentDevice() {
- return isCurrentDevice;
- }
-
- public void setCurrentDevice(boolean currentDevice) {
- isCurrentDevice = currentDevice;
- }
-}
diff --git a/src/main/java/io/castle/client/model/CastleUserDeviceContext.java b/src/main/java/io/castle/client/model/CastleUserDeviceContext.java
deleted file mode 100644
index 91c806e7..00000000
--- a/src/main/java/io/castle/client/model/CastleUserDeviceContext.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package io.castle.client.model;
-
-public class CastleUserDeviceContext {
- private String ip;
- private DeviceUserAgent userAgent;
- private String type;
-
- public String getIp() {
- return ip;
- }
-
- public void setIp(String ip) {
- this.ip = ip;
- }
-
- public DeviceUserAgent getUserAgent() {
- return userAgent;
- }
-
- public void setUserAgent(DeviceUserAgent userAgent) {
- this.userAgent = userAgent;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-}
diff --git a/src/main/java/io/castle/client/model/CastleUserDevices.java b/src/main/java/io/castle/client/model/CastleUserDevices.java
deleted file mode 100644
index fbc9399d..00000000
--- a/src/main/java/io/castle/client/model/CastleUserDevices.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.annotations.SerializedName;
-
-import java.util.List;
-
-public class CastleUserDevices {
- private int totalCount;
- @SerializedName("data")
- private List devices;
-
- public int getTotalCount() {
- return totalCount;
- }
-
- public List getDevices() {
- return devices;
- }
-
- public void setDevices(List devices) {
- this.devices = devices;
- this.totalCount = devices != null ? devices.size() : 0;
- }
-}
diff --git a/src/main/java/io/castle/client/model/DeviceUserAgent.java b/src/main/java/io/castle/client/model/DeviceUserAgent.java
deleted file mode 100644
index b92c6573..00000000
--- a/src/main/java/io/castle/client/model/DeviceUserAgent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package io.castle.client.model;
-
-public class DeviceUserAgent {
- private String raw;
- private String browser;
- private String version;
- private String os;
- private boolean mobile;
- private String platform;
- private String device;
- private String family;
-
- public String getRaw() {
- return raw;
- }
-
- public void setRaw(String raw) {
- this.raw = raw;
- }
-
- public String getBrowser() {
- return browser;
- }
-
- public void setBrowser(String browser) {
- this.browser = browser;
- }
-
- public String getVersion() {
- return version;
- }
-
- public void setVersion(String version) {
- this.version = version;
- }
-
- public String getOs() {
- return os;
- }
-
- public void setOs(String os) {
- this.os = os;
- }
-
- public boolean isMobile() {
- return mobile;
- }
-
- public void setMobile(boolean mobile) {
- this.mobile = mobile;
- }
-
- public String getPlatform() {
- return platform;
- }
-
- public void setPlatform(String platform) {
- this.platform = platform;
- }
-
- public String getDevice() {
- return device;
- }
-
- public void setDevice(String device) {
- this.device = device;
- }
-
- public String getFamily() {
- return family;
- }
-
- public void setFamily(String family) {
- this.family = family;
- }
-}
diff --git a/src/main/java/io/castle/client/model/ImpersonatePayload.java b/src/main/java/io/castle/client/model/ImpersonatePayload.java
deleted file mode 100644
index 161fe525..00000000
--- a/src/main/java/io/castle/client/model/ImpersonatePayload.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.JsonObject;
-
-public class ImpersonatePayload {
- private final String userId;
- private final JsonObject context;
- private final JsonObject properties;
-
- public ImpersonatePayload(String userId, String impersonator, JsonObject contextJson) {
- this.userId = userId;
- this.properties = new JsonObject();
- this.properties.addProperty("impersonator", impersonator);
- this.context = contextJson;
- }
-
- public ImpersonatePayload(String userId, JsonObject contextJson) {
- this.userId = userId;
- this.properties = new JsonObject();
- this.context = contextJson;
- }
-}
diff --git a/src/main/java/io/castle/client/model/Verdict.java b/src/main/java/io/castle/client/model/Verdict.java
deleted file mode 100644
index 8e29681e..00000000
--- a/src/main/java/io/castle/client/model/Verdict.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.JsonElement;
-
-/**
- * Model of the outcome of an authenticate call to the Castle API.
- */
-public class Verdict {
-
- /**
- * AuthenticateAction returned by a call to the CastleAPI, or configured by a failover strategy.
- */
- private AuthenticateAction action;
-
- /**
- * String representing a user ID associated with an authenticate call.
- */
- private String userId;
-
- /**
- * True if the SDK resorted the {@code AuthenticateFailoverStrategy} configured.
- */
- private boolean failover;
-
- /**
- * Explains the reason why the {@code AuthenticateFailoverStrategy} was used.
- */
- private String failoverReason;
-
- /**
- * String representing a device ID associated with an authenticate call.
- */
- private String deviceToken;
-
- /**
- * Float representing risk value associated with an authenticate call.
- */
- private float risk;
-
- /**
- * RiskPolicyResult representing risk policy used for generating this verdict.
- */
- private RiskPolicyResult riskPolicy;
-
- /**
- * JsonElement representing the full response of the server request
- */
- private JsonElement internal;
-
- public AuthenticateAction getAction() {
- return action;
- }
-
- public void setAction(AuthenticateAction action) {
- this.action = action;
- }
-
- public String getUserId() {
- return userId;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public boolean isFailover() {
- return failover;
- }
-
- public void setFailover(boolean failover) {
- this.failover = failover;
- }
-
- public String getFailoverReason() {
- return failoverReason;
- }
-
- public void setFailoverReason(String failoverReason) {
- this.failoverReason = failoverReason;
- }
-
- public String getDeviceToken() {
- return deviceToken;
- }
-
- public void setRisk(float risk) {
- this.risk = risk;
- }
-
- public float getRisk() {
- return risk;
- }
-
- public void setDeviceToken(String deviceToken) {
- this.deviceToken = deviceToken;
- }
-
- public void setInternal(JsonElement internal) {
- this.internal = internal;
- }
-
- public RiskPolicyResult getRiskPolicy() {
- return riskPolicy;
- }
-
- public void setRiskPolicy(RiskPolicyResult riskPolicy) {
- this.riskPolicy = riskPolicy;
- }
-
- public JsonElement getInternal() {
- return internal;
- }
-}
diff --git a/src/main/java/io/castle/client/model/generated/BaseList.java b/src/main/java/io/castle/client/model/generated/BaseList.java
index d6dc4aa8..b512229b 100755
--- a/src/main/java/io/castle/client/model/generated/BaseList.java
+++ b/src/main/java/io/castle/client/model/generated/BaseList.java
@@ -103,7 +103,7 @@ public BaseList defaultItemArchivationTime(Integer defaultItemArchivationTime) {
* @return defaultItemArchivationTime
**/
@ApiModelProperty(example = "2592000", value = "Default time, in seconds, after which Items in this List automatically gets archived after they are created. It is possible to override this value when creating the individual List Items")
- public int getDefaultItemArchivationTime() {
+ public Integer getDefaultItemArchivationTime() {
return defaultItemArchivationTime;
}
diff --git a/src/main/java/io/castle/client/model/generated/DeviceScreen.java b/src/main/java/io/castle/client/model/generated/DeviceScreen.java
index f25da37e..f1d14138 100755
--- a/src/main/java/io/castle/client/model/generated/DeviceScreen.java
+++ b/src/main/java/io/castle/client/model/generated/DeviceScreen.java
@@ -39,7 +39,7 @@ public DeviceScreen screen(Integer screen) {
* @return screen
**/
@ApiModelProperty(example = "2", value = "")
- public int getScreen() {
+ public Integer getScreen() {
return screen;
}
diff --git a/src/main/java/io/castle/client/model/generated/IP.java b/src/main/java/io/castle/client/model/generated/IP.java
index 2202660a..ce650978 100755
--- a/src/main/java/io/castle/client/model/generated/IP.java
+++ b/src/main/java/io/castle/client/model/generated/IP.java
@@ -51,7 +51,7 @@ public IP asn(Integer asn) {
* @return asn
**/
@ApiModelProperty(example = "14618", required = true, value = "")
- public int getAsn() {
+ public Integer getAsn() {
return asn;
}
diff --git a/src/main/java/io/castle/client/model/generated/ListItemsBatchResponse.java b/src/main/java/io/castle/client/model/generated/ListItemsBatchResponse.java
index c1b3d1ad..32e0e972 100755
--- a/src/main/java/io/castle/client/model/generated/ListItemsBatchResponse.java
+++ b/src/main/java/io/castle/client/model/generated/ListItemsBatchResponse.java
@@ -52,7 +52,7 @@ public ListItemsBatchResponse totalReceived(Integer totalReceived) {
* @return totalReceived
**/
@ApiModelProperty(required = true, value = "Total number of items received")
- public int getTotalReceived() {
+ public Integer getTotalReceived() {
return totalReceived;
}
@@ -70,7 +70,7 @@ public ListItemsBatchResponse totalProcessed(int totalProcessed) {
* @return totalProcessed
**/
@ApiModelProperty(required = true, value = "Total number of items processed without any errors")
- public int getTotalProcessed() {
+ public Integer getTotalProcessed() {
return totalProcessed;
}
@@ -88,7 +88,7 @@ public ListItemsBatchResponse created(int created) {
* @return created
**/
@ApiModelProperty(value = "Number of items created")
- public int getCreated() {
+ public Integer getCreated() {
return created;
}
@@ -106,7 +106,7 @@ public ListItemsBatchResponse updated(int updated) {
* @return updated
**/
@ApiModelProperty(value = "Number of items updated")
- public int getUpdated() {
+ public Integer getUpdated() {
return updated;
}
@@ -124,7 +124,7 @@ public ListItemsBatchResponse replaced(int replaced) {
* @return replaced
**/
@ApiModelProperty(value = "Total number of items replaced")
- public int getReplaced() {
+ public Integer getReplaced() {
return replaced;
}
@@ -142,7 +142,7 @@ public ListItemsBatchResponse errored(int errored) {
* @return errored
**/
@ApiModelProperty(value = "Total number of items that errored")
- public int getErrored() {
+ public Integer getErrored() {
return errored;
}
diff --git a/src/test/java/io/castle/client/AbstractCastleHttpLayerTest.java b/src/test/java/io/castle/client/AbstractCastleHttpLayerTest.java
index 23303633..4a7f4a5e 100644
--- a/src/test/java/io/castle/client/AbstractCastleHttpLayerTest.java
+++ b/src/test/java/io/castle/client/AbstractCastleHttpLayerTest.java
@@ -1,41 +1,31 @@
package io.castle.client;
-import io.castle.client.internal.backend.OkHttpFactory;
import io.castle.client.internal.config.CastleConfiguration;
import io.castle.client.internal.config.CastleConfigurationBuilder;
import io.castle.client.internal.config.CastleSdkInternalConfiguration;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleSdkConfigurationException;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockWebServer;
-import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import java.net.InetAddress;
-import java.util.concurrent.atomic.AtomicReference;
public abstract class AbstractCastleHttpLayerTest {
- private final AuthenticateFailoverStrategy testAuthenticateFailoverStrategy;
-
Castle sdk;
MockWebServer server;
HttpUrl testServerBaseUrl;
- protected AbstractCastleHttpLayerTest(AuthenticateFailoverStrategy testAuthenticateFailoverStrategy) {
- this.testAuthenticateFailoverStrategy = testAuthenticateFailoverStrategy;
- }
-
@Before
public void prepare() throws NoSuchFieldException, IllegalAccessException, CastleSdkConfigurationException, IOException {
//Given a mocked API server
server = new MockWebServer();
server.start(InetAddress.getByName("127.0.0.1"),0);
//Given a SDK instance
- sdk = new Castle(CastleSdkInternalConfiguration.getInternalConfiguration());
- CastleConfiguration configuration = sdk.getInternalConfiguration().getConfiguration();
+ Castle loaded = new Castle(CastleSdkInternalConfiguration.getInternalConfiguration());
+ CastleConfiguration configuration = loaded.getInternalConfiguration().getConfiguration();
testServerBaseUrl = server.url("/");
CastleConfiguration mockedApiConfiguration = CastleConfigurationBuilder.aConfigBuilder()
.withApiSecret(configuration.getApiSecret())
@@ -45,36 +35,18 @@ public void prepare() throws NoSuchFieldException, IllegalAccessException, Castl
.withAllowListHeaders(configuration.getAllowListHeaders())
.withCastleAppId(configuration.getCastleAppId())
.withBackendProvider(configuration.getBackendProvider())
- .withAuthenticateFailoverStrategy(testAuthenticateFailoverStrategy)
.withTimeout(configuration.getTimeout())
.build();
+ loaded.close();
sdk = Castle.initialize(mockedApiConfiguration);
}
@After
public void tearDown() throws Exception {
- server.shutdown();
- }
-
-
- protected void waitForValueAndVerify(AtomicReference result, T expected) {
- T extracted = waitForValue(result);
- Assertions.assertThat(extracted).usingRecursiveComparison().isEqualTo(expected);
- }
-
- protected T waitForValue(AtomicReference result) {
- T value = result.get();
- int maxNrOfSleeps = 10;
- while (value == null && maxNrOfSleeps > 0) {
- maxNrOfSleeps = maxNrOfSleeps - 1;
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- value = result.get();
+ if (sdk != null) {
+ sdk.close();
}
- Assertions.assertThat(value).isNotNull();
- return value;
+ server.shutdown();
}
}
diff --git a/src/test/java/io/castle/client/CastleAuthenticateHttpTest.java b/src/test/java/io/castle/client/CastleAuthenticateHttpTest.java
deleted file mode 100644
index 4e57ce44..00000000
--- a/src/test/java/io/castle/client/CastleAuthenticateHttpTest.java
+++ /dev/null
@@ -1,541 +0,0 @@
-package io.castle.client;
-
-import com.google.gson.JsonParser;
-import io.castle.client.internal.utils.VerdictBuilder;
-import io.castle.client.model.*;
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.utils.SDKVersion;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Assert;
-import org.junit.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import com.google.common.collect.ImmutableMap;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleAuthenticateHttpTest extends AbstractCastleHttpLayerTest {
-
- private static final String DENY_RESPONSE = "{\n" +
- " \"action\": \"deny\",\n" +
- " \"user_id\": \"12345\",\n" +
- " \"device_token\": \"abcdefg1234\",\n" +
- " \"risk_policy\": {\n" +
- " \"id\": \"q-rbeMzBTdW2Fd09sbz55A\",\n" +
- " \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\n" +
- " \"name\": \"Block Users from X\",\n" +
- " \"type\": \"bot\"\n" +
- " }\n" +
- "}";
-
- private static final String DENY_RESPONSE_NO_POLICY = "{\n" +
- " \"action\": \"deny\",\n" +
- " \"user_id\": \"12345\",\n" +
- " \"device_token\": \"abcdefg1234\"\n" +
- "}";
-
- public CastleAuthenticateHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test
- public void authenticateAsyncEndpointWithNullUserIdPayload() throws InterruptedException, JSONException {
- // Given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
- // and an authenticate request is made
- sdk.onRequest(request).authenticateAsync(
- CastleMessage.builder("$login.succeeded").build(),
- handler
- );
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- public void authenticateAsyncEndpontWithPayload() throws InterruptedException, JSONException {
- // Given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
- // and an authenticate request is made
- sdk.onRequest(request).authenticateAsync(
- CastleMessage.builder("$login.succeeded").userId("12345").build(),
- handler
- );
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void authenticationAsyncEndpointTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
- String id = "12345";
- String event = "$login.succeeded";
- String deviceToken = "abcdefg1234";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
- // and an authenticate request is made
- sdk.onRequest(request).authenticateAsync(event, id, handler);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- String json = "{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}";
-
- JSONAssert.assertEquals(json, body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
-
- RiskPolicyResult riskPolicyResult = new CastleGsonModel().getGson().fromJson("{\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}", RiskPolicyResult.class);
-
- // and
- Verdict expected = VerdictBuilder.success()
- .withAction(AuthenticateAction.DENY)
- .withUserId("12345")
- .withDeviceToken(deviceToken)
- .withRiskPolicy(riskPolicyResult)
- .withInternal(JsonParser.parseString("{\"action\":\"deny\",\"user_id\":\"12345\",\"device_token\":\"abcdefg1234\", \"risk_policy\": {\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}}"))
- .build();
- waitForValueAndVerify(result, expected);
- }
-
- @Test
- public void authenticationAsyncEndpointNoRiskPolicyTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE_NO_POLICY));
- String id = "12345";
- String event = "$login.succeeded";
- String deviceToken = "abcdefg1234";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
- // and an authenticate request is made
- sdk.onRequest(request).authenticateAsync(event, id, handler);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- String json = "{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}";
-
- JSONAssert.assertEquals(json, body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
-
- // and
- Verdict expected = VerdictBuilder.success()
- .withAction(AuthenticateAction.DENY)
- .withUserId("12345")
- .withDeviceToken(deviceToken)
- .withInternal(JsonParser.parseString("{\"action\":\"deny\",\"user_id\":\"12345\",\"device_token\":\"abcdefg1234\"}"))
- .build();
- waitForValueAndVerify(result, expected);
- }
-
- @Test
- public void authenticationAsyncEndpointWithPropertiesAndTraitsTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
- String id = "12345";
- String event = "$login.succeeded";
- String deviceToken = "abcdefg1234";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
- // and an authenticate request is made
- sdk.onRequest(request).authenticateAsync(event, id, ImmutableMap.builder()
- .put("b",0)
- .build(), ImmutableMap.builder()
- .put("y",0)
- .build(), handler);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"properties\":{\"b\":0},\"user_id\":\"12345\",\"user_traits\":{\"y\":0},\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
-
- RiskPolicyResult riskPolicyResult = new CastleGsonModel().getGson().fromJson("{\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}", RiskPolicyResult.class);
-
- // and
- Verdict expected = VerdictBuilder.success()
- .withAction(AuthenticateAction.DENY)
- .withUserId("12345")
- .withDeviceToken(deviceToken)
- .withRiskPolicy(riskPolicyResult)
- .withInternal(JsonParser.parseString("{\"action\":\"deny\",\"user_id\":\"12345\",\"device_token\":\"abcdefg1234\", \"risk_policy\": {\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}}"))
- .build();
- waitForValueAndVerify(result, expected);
- }
-
-
- @Test
- public void authenticationAsyncEndpointProvideDefaultValueOnTimeoutErrorTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- String id = "12345";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
- // and an authenticate request is made
- sdk.onRequest(request).authenticateAsync(event, id, handler);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
-
- // and
- Verdict expected = VerdictBuilder.failover("timeout")
- .withAction(AuthenticateAction.CHALLENGE)
- .withUserId(id)
- .build();
-
- verifyFailoverResponse(result, expected, false);
- }
-
-
- @Test
- public void authenticationEndpointDefaultValueOnTimeoutErrorTest() throws InterruptedException, JSONException {
- //given the backed will timeout
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- String id = "12345";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(event, id);
-
- // then
- Verdict expected = VerdictBuilder.failover("timeout")
- .withAction(AuthenticateAction.CHALLENGE)
- .withUserId(id)
- .build();
- verifyFailoverResponse(verdict, expected, false);
-
- // and
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void authenticationEndpointWithPayload() throws InterruptedException, JSONException {
- // Given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(
- CastleMessage.builder("$login.succeeded").userId("12345").build()
- );
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void authenticationEndpointTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
- String id = "12345";
- String event = "$login.succeeded";
- String deviceToken = "abcdefg1234";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(event, id);
-
- RiskPolicyResult riskPolicyResult = new CastleGsonModel().getGson().fromJson("{\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}", RiskPolicyResult.class);
-
- // then
- Verdict expected = VerdictBuilder.success()
- .withAction(AuthenticateAction.DENY)
- .withUserId(id)
- .withDeviceToken(deviceToken)
- .withRiskPolicy(riskPolicyResult)
- .withInternal(JsonParser.parseString("{\"action\":\"deny\",\"user_id\":\"12345\",\"device_token\":\"abcdefg1234\", \"risk_policy\": {\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}}"))
- .build();
- Assertions.assertThat(verdict).usingRecursiveComparison().isEqualTo(expected);
-
- // and
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void authenticationEndpointWithPropertiesAndTraitsTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse().setBody(DENY_RESPONSE));
- String id = "12345";
- String event = "$login.succeeded";
- String deviceToken = "abcdefg1234";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(event, id, ImmutableMap.builder()
- .put("a","valueA")
- .put("b",123456)
- .build(), ImmutableMap.builder()
- .put("x","valueX")
- .put("y",654321)
- .build());
-
- RiskPolicyResult riskPolicyResult = new CastleGsonModel().getGson().fromJson("{\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}", RiskPolicyResult.class);
-
- // then
- Verdict expected = VerdictBuilder.success()
- .withAction(AuthenticateAction.DENY)
- .withUserId(id)
- .withDeviceToken(deviceToken)
- .withRiskPolicy(riskPolicyResult)
- .withInternal(JsonParser.parseString("{\"action\":\"deny\",\"user_id\":\"12345\",\"device_token\":\"abcdefg1234\", \"risk_policy\": {\"id\": \"q-rbeMzBTdW2Fd09sbz55A\", \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\"name\": \"Block Users from X\",\"type\": \"bot\"}}"))
- .build();
- Assertions.assertThat(verdict).usingRecursiveComparison().isEqualTo(expected);
-
- // and
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"properties\":{\"a\":\"valueA\",\"b\":123456},\"user_id\":\"12345\",\"user_traits\":{\"x\":\"valueX\",\"y\":654321},\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test(expected = CastleRuntimeException.class)
- public void authenticationThrowExceptionWhenBackendReturnErrors() throws InterruptedException {
- //given a 403 response from backend
- server.enqueue(new MockResponse().setResponseCode(403));
- String id = "12345";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(event, id, ImmutableMap.builder()
- .put("a","valueA")
- .put("b",123456)
- .build(), null);
-
- // then a exception is send to the client code because of bad response from the castle backend
- }
-
- @Test
- public void authenticationUseDefaultOnBackendErrorTest() throws InterruptedException, JSONException {
- //given a 403 response from backend
- server.enqueue(new MockResponse().setResponseCode(500));
- String id = "12345";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(event, id, ImmutableMap.builder()
- .put("a","valueA")
- .put("b",123456)
- .build(), null);
-
- // then
- Verdict expected = VerdictBuilder.failover("Client Error")
- .withAction(AuthenticateAction.CHALLENGE)
- .withUserId(id)
- .build();
- verifyFailoverResponse(verdict, expected, false);
-
- // and
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"properties\":{\"a\":\"valueA\",\"b\":123456},\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test(expected = CastleRuntimeException.class)
- public void authenticationUseDefaultOnBadResponseFormatTestEmptyCase() throws InterruptedException {
- //given a response do not match the transport contract
- testIlegalJsonForAuthenticate("{}");
-
- }
- @Test(expected = CastleRuntimeException.class)
- public void authenticationUseDefaultOnBadResponseFormatTestInvalidActionUseCase() throws InterruptedException {
- //given a response do not match the transport contract
- testIlegalJsonForAuthenticate("{\"action\":\"action\"}");
-
- }
-
- private void testIlegalJsonForAuthenticate(String illegalJsonBody) {
- server.enqueue(new MockResponse().setBody(illegalJsonBody));
- String id = "12345";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an authenticate request is made
- Verdict verdict = sdk.onRequest(request).authenticate(event, id, ImmutableMap.builder()
- .put("a","valueA")
- .put("b",123456)
- .build(), null);
-
- // then a illegal json failover is provided
- Verdict expected = VerdictBuilder.failover("Illegal json format")
- .withAction(AuthenticateAction.CHALLENGE)
- .withUserId(id)
- .build();
- verifyFailoverResponse(verdict, expected, true);
- }
-
- /**
- * Verify that the async Verdict value match the expected values.
- * The Failover reason depend from the JVM implementation, so we only check that is not null.
- *
- * @param result
- * @param expected
- */
- private void verifyFailoverResponse(AtomicReference result, Verdict expected, boolean expectedExactReasonMatch) {
- Verdict extractedVerdict = waitForValue(result);
- verifyFailoverResponse(extractedVerdict, expected, expectedExactReasonMatch);
- }
-
- /**
- * Sync version of the verification for Verdict
- *
- * @param extractedVerdict
- * @param expected
- */
- private void verifyFailoverResponse(Verdict extractedVerdict, Verdict expected, boolean expectedExactReasonMatch) {
- Assertions.assertThat(extractedVerdict.getAction()).isEqualTo(expected.getAction());
- Assertions.assertThat(extractedVerdict.getUserId()).isEqualTo(expected.getUserId());
- Assertions.assertThat(extractedVerdict.isFailover()).isTrue();
- if (expectedExactReasonMatch) {
- Assertions.assertThat(extractedVerdict.getFailoverReason()).isEqualTo(expected.getFailoverReason());
- } else {
- Assertions.assertThat(extractedVerdict.getFailoverReason()).isNotEmpty();
- }
- }
-}
diff --git a/src/test/java/io/castle/client/CastleDeviceApproveHttpTest.java b/src/test/java/io/castle/client/CastleDeviceApproveHttpTest.java
deleted file mode 100644
index 6809d281..00000000
--- a/src/test/java/io/castle/client/CastleDeviceApproveHttpTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleDeviceApproveHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleDeviceApproveHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void approveDevice() throws InterruptedException {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody(DeviceUtils.getDefaultDeviceJSON());
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an report device request is made
- CastleUserDevice device = sdk.onRequest(request).approve("deviceToken");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/devices/deviceToken/approve"), recordedRequest.getRequestUrl());
- Assert.assertEquals("PUT", recordedRequest.getMethod());
-
- // and the correct CastleUserDevice model object is extracted
- CastleUserDevice expected = DeviceUtils.createExpectedDevice();
-
- Assertions.assertThat(device).usingRecursiveComparison().isEqualTo(expected);
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void approveDeviceTimeoutTest() {
-
- // given backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token/approve request is made
- sdk.onRequest(request).approve(deviceToken);
- }
-
- @Test(expected = CastleRuntimeException.class)
- public void testExceptionWithServerError () {
-
- // given a server failure
- server.enqueue(new MockResponse().setResponseCode(500));
- // And a request
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token/approve request is made
- sdk.onRequest(request).approve(deviceToken);
-
- }
-}
diff --git a/src/test/java/io/castle/client/CastleDeviceHttpTest.java b/src/test/java/io/castle/client/CastleDeviceHttpTest.java
deleted file mode 100644
index 8ac91f25..00000000
--- a/src/test/java/io/castle/client/CastleDeviceHttpTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleDeviceHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleDeviceHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void getDevice() throws Exception {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody(DeviceUtils.getDefaultDeviceJSON());
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an devices request is made
- CastleUserDevice device = sdk.onRequest(request).device("deviceToken");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/devices/deviceToken"), recordedRequest.getRequestUrl());
-
- // and the correct CastleUserDevice model object is extracted
- CastleUserDevice expected = DeviceUtils.createExpectedDevice();
-
- Assertions.assertThat(device).usingRecursiveComparison().isEqualTo(expected);
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void getDeviceTimeoutTest() {
-
- // given backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token request is made
- sdk.onRequest(request).device(deviceToken);
- }
-
- @Test(expected = CastleRuntimeException.class)
- public void testExceptionWithServerError () {
-
- // given a server failure
- server.enqueue(new MockResponse().setResponseCode(500));
- // And a request
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token request is made
- sdk.onRequest(request).device(deviceToken);
-
- }
-
- @Test
- public void getDeviceNotFoundTest () {
-
- // given a server failure
- server.enqueue(new MockResponse().setResponseCode(404));
- // And a request
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token request is made
- CastleUserDevice userDevice = sdk.onRequest(request).device(deviceToken);
-
- Assert.assertNull(userDevice);
- }
-}
diff --git a/src/test/java/io/castle/client/CastleDeviceReportHttpTest.java b/src/test/java/io/castle/client/CastleDeviceReportHttpTest.java
deleted file mode 100644
index d927a4cb..00000000
--- a/src/test/java/io/castle/client/CastleDeviceReportHttpTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleDeviceReportHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleDeviceReportHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void reportDevice() throws InterruptedException {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody(DeviceUtils.getDefaultDeviceJSON());
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an report device request is made
- CastleUserDevice device = sdk.onRequest(request).report("deviceToken");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/devices/deviceToken/report"), recordedRequest.getRequestUrl());
- Assert.assertEquals("PUT", recordedRequest.getMethod());
-
- // and the correct CastleUserDevice model object is extracted
- CastleUserDevice expected = DeviceUtils.createExpectedDevice();
-
- Assertions.assertThat(device).usingRecursiveComparison().isEqualTo(expected);
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void reportDeviceTimeoutTest() {
-
- // given backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token/report request is made
- sdk.onRequest(request).report(deviceToken);
- }
-
- @Test(expected = CastleRuntimeException.class)
- public void testExceptionWithServerError () {
-
- // given a server failure
- server.enqueue(new MockResponse().setResponseCode(500));
- // And a request
- HttpServletRequest request = new MockHttpServletRequest();
- String deviceToken = "deviceToken";
-
- //when a v1/devices/:device_token/report request is made
- sdk.onRequest(request).report(deviceToken);
-
- }
-}
diff --git a/src/test/java/io/castle/client/CastleDevicesHttpTest.java b/src/test/java/io/castle/client/CastleDevicesHttpTest.java
deleted file mode 100644
index 68be962d..00000000
--- a/src/test/java/io/castle/client/CastleDevicesHttpTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleDevicesHttpTest extends AbstractCastleHttpLayerTest {
-
- private CastleGsonModel model = new CastleGsonModel();
-
- public CastleDevicesHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void getDevices() throws Exception {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody("{\"total_count\":1,\"data\":[" + DeviceUtils.getDefaultDeviceJSON() + "]}");
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an devices request is made
- CastleUserDevices devices = sdk.onRequest(request).userDevices("userId");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/users/userId/devices"), recordedRequest.getRequestUrl());
-
- // and the correct CastleUserDevice model object is extracted
- CastleUserDevices expected = DeviceUtils.createExpectedDevices();
-
- Assertions.assertThat(devices).usingRecursiveComparison().isEqualTo(expected);
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void getDevicesTimeoutTest() {
-
- // given backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- HttpServletRequest request = new MockHttpServletRequest();
- String userId = "userId";
-
- //when a v1/users/:user_id/devices request is made
- sdk.onRequest(request).userDevices(userId);
- }
-
- @Test(expected = CastleRuntimeException.class)
- public void testExceptionWithServerError () {
-
- // given a server failure
- server.enqueue(new MockResponse().setResponseCode(500));
- // And a request
- HttpServletRequest request = new MockHttpServletRequest();
- String userId = "userId";
-
- //when a v1/users/:user_id/devices request is made
- sdk.onRequest(request).userDevices(userId);
-
- }
-
- @Test
- public void getDevicesNotFoundTest () {
-
- // given a server failure
- server.enqueue(new MockResponse().setResponseCode(404));
- // And a request
- HttpServletRequest request = new MockHttpServletRequest();
- String userId = "userId";
-
- //when a v1/users/:user_id/devices request is made
- CastleUserDevices userDevices = sdk.onRequest(request).userDevices(userId);
-
- Assert.assertNull(userDevices);
- }
-}
diff --git a/src/test/java/io/castle/client/CastleEventsHttpTest.java b/src/test/java/io/castle/client/CastleEventsHttpTest.java
new file mode 100644
index 00000000..5c3894e0
--- /dev/null
+++ b/src/test/java/io/castle/client/CastleEventsHttpTest.java
@@ -0,0 +1,70 @@
+package io.castle.client;
+
+import com.google.common.collect.ImmutableMap;
+import io.castle.client.model.CastleResponse;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.assertj.core.api.Assertions;
+import org.json.JSONException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+public class CastleEventsHttpTest extends AbstractCastleHttpLayerTest {
+
+ @Test
+ public void eventsSchema() throws InterruptedException {
+ server.enqueue(new MockResponse().setBody("{}"));
+ HttpServletRequest request = new MockHttpServletRequest();
+
+ CastleResponse response = sdk.onRequest(request).eventsSchema();
+ if (response == null) {
+ Assertions.fail("error on request");
+ }
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ Assert.assertEquals(testServerBaseUrl.resolve("v1/events/schema"), recordedRequest.getRequestUrl());
+ Assert.assertEquals("GET", recordedRequest.getMethod());
+ }
+
+ @Test
+ public void queryEvents() throws InterruptedException, JSONException {
+ server.enqueue(new MockResponse().setBody("{}"));
+ HttpServletRequest request = new MockHttpServletRequest();
+
+ CastleResponse response = sdk.onRequest(request).queryEvents(ImmutableMap.builder()
+ .put("type", "$login")
+ .build());
+ if (response == null) {
+ Assertions.fail("error on request");
+ }
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ String body = recordedRequest.getBody().readUtf8();
+ Assert.assertEquals(testServerBaseUrl.resolve("v1/events/query"), recordedRequest.getRequestUrl());
+ Assert.assertEquals("POST", recordedRequest.getMethod());
+ JSONAssert.assertEquals("{\"type\":\"$login\"}", body, false);
+ }
+
+ @Test
+ public void groupEvents() throws InterruptedException, JSONException {
+ server.enqueue(new MockResponse().setBody("{}"));
+ HttpServletRequest request = new MockHttpServletRequest();
+
+ CastleResponse response = sdk.onRequest(request).groupEvents(ImmutableMap.builder()
+ .put("field", "name")
+ .build());
+ if (response == null) {
+ Assertions.fail("error on request");
+ }
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ String body = recordedRequest.getBody().readUtf8();
+ Assert.assertEquals(testServerBaseUrl.resolve("v1/events/group"), recordedRequest.getRequestUrl());
+ Assert.assertEquals("POST", recordedRequest.getMethod());
+ JSONAssert.assertEquals("{\"field\":\"name\"}", body, false);
+ }
+}
diff --git a/src/test/java/io/castle/client/CastleExceptionTest.java b/src/test/java/io/castle/client/CastleExceptionTest.java
index 04665ecb..a4fb1aa9 100644
--- a/src/test/java/io/castle/client/CastleExceptionTest.java
+++ b/src/test/java/io/castle/client/CastleExceptionTest.java
@@ -120,6 +120,51 @@ public void invalidJsonError() {
OkHttpExceptionUtil.handle(response);
}
+ @Test(expected = CastleApiPaymentRequiredException.class)
+ public void paymentRequiredError() {
+ //Given
+ Response response = new Response.Builder()
+ .code(402)
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .message("Payment Required")
+ .body(ResponseBody.create("{\"type\":\"credit_exhausted\",\"message\":\"Included usage credit for this billing period has been used up\"}", JsonMediaType))
+ .build();
+
+ OkHttpExceptionUtil.handle(response);
+ }
+
+ @Test
+ public void emptyBodyIsAccepted() throws IOException {
+ Response response = new Response.Builder()
+ .code(204)
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .message("No Content")
+ .body(ResponseBody.create("", JsonMediaType))
+ .build();
+
+ CastleResponse castleResponse = new CastleResponse(response);
+
+ Assert.assertTrue(castleResponse.isSuccessful());
+ Assert.assertTrue(castleResponse.json().isJsonNull());
+ }
+
+ @Test
+ public void missingBodyIsAccepted() throws IOException {
+ Response response = new Response.Builder()
+ .code(204)
+ .request(new Request.Builder().url("http://localhost").build())
+ .protocol(Protocol.HTTP_1_1)
+ .message("No Content")
+ .build();
+
+ CastleResponse castleResponse = new CastleResponse(response);
+
+ Assert.assertTrue(castleResponse.isSuccessful());
+ Assert.assertTrue(castleResponse.json().isJsonNull());
+ }
+
@Test(expected = CastleApiNotFoundException.class)
public void notFoundError() {
//Given
diff --git a/src/test/java/io/castle/client/CastleFilterHttpTest.java b/src/test/java/io/castle/client/CastleFilterHttpTest.java
index 8c2fd85e..1950089c 100644
--- a/src/test/java/io/castle/client/CastleFilterHttpTest.java
+++ b/src/test/java/io/castle/client/CastleFilterHttpTest.java
@@ -2,8 +2,6 @@
import com.google.gson.JsonParser;
import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.generated.*;
import jakarta.servlet.http.HttpServletRequest;
import okhttp3.mockwebserver.MockResponse;
@@ -18,10 +16,6 @@
public class CastleFilterHttpTest extends AbstractCastleHttpLayerTest {
- public CastleFilterHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
@Test public void filter() throws InterruptedException {
MockResponse mockResponse = new MockResponse();
mockResponse.setBody("{\n" +
@@ -242,7 +236,7 @@ public CastleFilterHttpTest() {
Assert.assertEquals("en", response.getDevice().getSoftware().getLanguages().get(1));
Assert.assertEquals("91.0.4472", response.getDevice().getSoftware().getVersion().getFull());
Assert.assertEquals("91", response.getDevice().getSoftware().getVersion().getMajor());
- Assert.assertEquals(2, response.getDevice().getScreen().getScreen());
+ Assert.assertEquals(Integer.valueOf(2), response.getDevice().getScreen().getScreen());
Assert.assertEquals("landscape", response.getDevice().getScreen().getOrientation());
Assert.assertEquals("America/New_York", response.getDevice().getTimezone().getName());
Assert.assertEquals(-300, response.getDevice().getTimezone().getOffset());
@@ -270,7 +264,7 @@ public CastleFilterHttpTest() {
Assert.assertFalse(response.getEmail().getDomainDetails().getDmarcRecord().isExists());
Assert.assertFalse(response.getEmail().getDomainDetails().getMxRecords().isNullMx());
Assert.assertEquals("/v1/risk", response.getEndpoint());
- Assert.assertEquals(14618, response.getIp().getAsn());
+ Assert.assertEquals(Integer.valueOf(14618), response.getIp().getAsn());
Assert.assertEquals("Ashburn", response.getIp().getLocation().getCity());
Assert.assertEquals("NA", response.getIp().getLocation().getContinentCode());
Assert.assertEquals("US", response.getIp().getLocation().getCountryCode());
diff --git a/src/test/java/io/castle/client/CastleGenericAPIHttpTest.java b/src/test/java/io/castle/client/CastleGenericAPIHttpTest.java
index a510c887..ebcb67ea 100644
--- a/src/test/java/io/castle/client/CastleGenericAPIHttpTest.java
+++ b/src/test/java/io/castle/client/CastleGenericAPIHttpTest.java
@@ -1,14 +1,8 @@
package io.castle.client;
import com.google.common.collect.ImmutableMap;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.internal.utils.CastleContextBuilder;
import io.castle.client.internal.utils.Timestamp;
-import io.castle.client.internal.utils.VerdictBuilder;
import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
import io.castle.client.utils.SDKVersion;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
@@ -21,9 +15,6 @@
import org.springframework.mock.web.MockHttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
public class CastleGenericAPIHttpTest extends AbstractCastleHttpLayerTest {
@@ -39,10 +30,6 @@ public class CastleGenericAPIHttpTest extends AbstractCastleHttpLayerTest {
" }\n" +
"}";
- public CastleGenericAPIHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
@Test
public void postRequest() throws InterruptedException, JSONException {
// Given
@@ -54,8 +41,7 @@ public void postRequest() throws InterruptedException, JSONException {
CastleContext payload = sdk.contextBuilder()
.build();
- // and an authenticate request is made
- CastleResponse response = sdk.onRequest(request).post("/v1/authenticate", ImmutableMap.builder()
+ CastleResponse response = sdk.onRequest(request).post("/v1/risk", ImmutableMap.builder()
.put("event", "$login.succeeded")
.put("userId", "12345")
.put("context", payload)
diff --git a/src/test/java/io/castle/client/CastleImpersonateEndHttpTest.java b/src/test/java/io/castle/client/CastleImpersonateEndHttpTest.java
deleted file mode 100644
index 8474d69d..00000000
--- a/src/test/java/io/castle/client/CastleImpersonateEndHttpTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleImpersonateEndHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleImpersonateEndHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void impersonateEnd() throws Exception {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody("{\"success\":true}");
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an devices request is made
- CastleSuccess result = sdk.onRequest(request).impersonateEnd("userId");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/impersonate"), recordedRequest.getRequestUrl());
- Assert.assertEquals("DELETE", recordedRequest.getMethod());
-
- // and the correct Success model object is extracted
- CastleSuccess expected = new CastleSuccess();
- expected.setSuccess(true);
-
- Assertions.assertThat(result).usingRecursiveComparison().isEqualTo(expected);
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void impersonateEndTimeoutTest() {
-
- // given backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- HttpServletRequest request = new MockHttpServletRequest();
- String userId = "userId";
-
- //when a v1/impersonate request is made
- sdk.onRequest(request).impersonateStart(userId);
- }
-}
diff --git a/src/test/java/io/castle/client/CastleImpersonateStartHttpTest.java b/src/test/java/io/castle/client/CastleImpersonateStartHttpTest.java
deleted file mode 100644
index f2cb0eb6..00000000
--- a/src/test/java/io/castle/client/CastleImpersonateStartHttpTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleImpersonateStartHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleImpersonateStartHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void impersonateStart() throws Exception {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody("{\"success\":true}");
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an devices request is made
- CastleSuccess result = sdk.onRequest(request).impersonateStart("userId");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/impersonate"), recordedRequest.getRequestUrl());
- Assert.assertEquals("POST", recordedRequest.getMethod());
-
- // and the correct Success model object is extracted
- CastleSuccess expected = new CastleSuccess();
- expected.setSuccess(true);
-
- Assertions.assertThat(result).usingRecursiveComparison().isEqualTo(expected);
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void impersonateStartTimeoutTest() {
-
- // given backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- HttpServletRequest request = new MockHttpServletRequest();
- String userId = "userId";
-
- //when a review request is made
- sdk.onRequest(request).impersonateStart(userId);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/io/castle/client/CastleListItemsTest.java b/src/test/java/io/castle/client/CastleListItemsTest.java
index a3c91e91..2a04118f 100644
--- a/src/test/java/io/castle/client/CastleListItemsTest.java
+++ b/src/test/java/io/castle/client/CastleListItemsTest.java
@@ -1,7 +1,5 @@
package io.castle.client;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleResponse;
import io.castle.client.model.generated.*;
import jakarta.servlet.http.HttpServletRequest;
@@ -18,10 +16,6 @@
public class CastleListItemsTest extends AbstractCastleHttpLayerTest {
- public CastleListItemsTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
@Test
public void createListItem() throws InterruptedException {
MockResponse mockResponse = new MockResponse();
@@ -89,12 +83,12 @@ public void createOrUpdateListItems() throws InterruptedException {
ListItemsBatchResponse response = sdk.onRequest(request).createOrUpdateListItems("2ee938c8-24c2-4c26-9d25-19511dd75029", items);
// Check response object
- Assert.assertEquals(135, response.getTotalReceived());
- Assert.assertEquals(130, response.getTotalProcessed());
- Assert.assertEquals(5, response.getErrored());
- Assert.assertEquals(10, response.getReplaced());
- Assert.assertEquals(20, response.getUpdated());
- Assert.assertEquals(100, response.getCreated());
+ Assert.assertEquals(Integer.valueOf(135), response.getTotalReceived());
+ Assert.assertEquals(Integer.valueOf(130), response.getTotalProcessed());
+ Assert.assertEquals(Integer.valueOf(5), response.getErrored());
+ Assert.assertEquals(Integer.valueOf(10), response.getReplaced());
+ Assert.assertEquals(Integer.valueOf(20), response.getUpdated());
+ Assert.assertEquals(Integer.valueOf(100), response.getCreated());
// Then
RecordedRequest recordedRequest = server.takeRequest();
diff --git a/src/test/java/io/castle/client/CastleListsHttpTest.java b/src/test/java/io/castle/client/CastleListsHttpTest.java
index 548be6f8..bd9675b7 100644
--- a/src/test/java/io/castle/client/CastleListsHttpTest.java
+++ b/src/test/java/io/castle/client/CastleListsHttpTest.java
@@ -2,8 +2,6 @@
import com.google.gson.JsonParser;
import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.generated.*;
import jakarta.servlet.http.HttpServletRequest;
import okhttp3.mockwebserver.MockResponse;
@@ -19,10 +17,6 @@
public class CastleListsHttpTest extends AbstractCastleHttpLayerTest {
- public CastleListsHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
@Test
public void updateList() throws InterruptedException {
MockResponse mockResponse = new MockResponse();
@@ -51,7 +45,7 @@ public void updateList() throws InterruptedException {
Assert.assertEquals("Malicious IPs", response.getName());
Assert.assertEquals("We block these IPs from withdrawing funds. Please be careful.", response.getDescription());
Assert.assertEquals(ListColor.RED, response.getColor());
- Assert.assertEquals(2592000, response.getDefaultItemArchivationTime());
+ Assert.assertEquals(Integer.valueOf(2592000), response.getDefaultItemArchivationTime());
Assert.assertEquals("2ee938c8-24c2-4c26-9d25-19511dd75029", response.getId());
Assert.assertEquals("device.fingerprint", response.getPrimaryField());
Assert.assertEquals("ip.value", response.getSecondaryField());
@@ -94,7 +88,7 @@ public void listAllLists() throws InterruptedException {
Assert.assertEquals("Malicious IPs", list.getName());
Assert.assertEquals("We block these IPs from withdrawing funds. Please be careful.", list.getDescription());
Assert.assertEquals(ListColor.RED, list.getColor());
- Assert.assertEquals(2592000, list.getDefaultItemArchivationTime());
+ Assert.assertEquals(Integer.valueOf(2592000), list.getDefaultItemArchivationTime());
Assert.assertEquals("2ee938c8-24c2-4c26-9d25-19511dd75029", list.getId());
Assert.assertEquals("device.fingerprint", list.getPrimaryField());
Assert.assertEquals("ip.value", list.getSecondaryField());
@@ -134,7 +128,7 @@ public void listAllLists() throws InterruptedException {
Assert.assertEquals("Malicious IPs", response.getName());
Assert.assertEquals("We block these IPs from withdrawing funds. Please be careful.", response.getDescription());
Assert.assertEquals(ListColor.RED, response.getColor());
- Assert.assertEquals(2592000, response.getDefaultItemArchivationTime());
+ Assert.assertEquals(Integer.valueOf(2592000), response.getDefaultItemArchivationTime());
Assert.assertEquals("2ee938c8-24c2-4c26-9d25-19511dd75029", response.getId());
Assert.assertEquals("device.fingerprint", response.getPrimaryField());
Assert.assertEquals("ip.value", response.getSecondaryField());
@@ -197,7 +191,7 @@ public void searchList() throws InterruptedException {
Assert.assertEquals("Malicious IPs", list.getName());
Assert.assertEquals("We block these IPs from withdrawing funds. Please be careful.", list.getDescription());
Assert.assertEquals(ListColor.RED, list.getColor());
- Assert.assertEquals(2592000, list.getDefaultItemArchivationTime());
+ Assert.assertEquals(Integer.valueOf(2592000), list.getDefaultItemArchivationTime());
Assert.assertEquals("2ee938c8-24c2-4c26-9d25-19511dd75029", list.getId());
Assert.assertEquals("device.fingerprint", list.getPrimaryField());
Assert.assertEquals("ip.value", list.getSecondaryField());
@@ -239,7 +233,7 @@ public void createList() throws InterruptedException {
Assert.assertEquals("Malicious IPs", response.getName());
Assert.assertEquals("We block these IPs from withdrawing funds. Please be careful.", response.getDescription());
Assert.assertEquals(ListColor.RED, response.getColor());
- Assert.assertEquals(2592000, response.getDefaultItemArchivationTime());
+ Assert.assertEquals(Integer.valueOf(2592000), response.getDefaultItemArchivationTime());
Assert.assertEquals("2ee938c8-24c2-4c26-9d25-19511dd75029", response.getId());
Assert.assertEquals("device.fingerprint", response.getPrimaryField());
Assert.assertEquals("ip.value", response.getSecondaryField());
@@ -421,4 +415,13 @@ public void compareListResponseJson() {
// Compare the JSON strings
Assert.assertEquals(JsonParser.parseString(providedJson), JsonParser.parseString(listJson));
}
+
+ @Test
+ public void listResponseAllowsMissingArchivationTime() {
+ ListResponse response = new CastleGsonModel().getGson().fromJson(
+ "{\"name\":\"demo\",\"id\":\"abc\",\"primary_field\":\"user.email\"}",
+ ListResponse.class);
+
+ Assert.assertNull(response.getDefaultItemArchivationTime());
+ }
}
diff --git a/src/test/java/io/castle/client/CastleLogHttpTest.java b/src/test/java/io/castle/client/CastleLogHttpTest.java
index d3d1a579..534adadc 100644
--- a/src/test/java/io/castle/client/CastleLogHttpTest.java
+++ b/src/test/java/io/castle/client/CastleLogHttpTest.java
@@ -1,11 +1,8 @@
package io.castle.client;
import com.google.gson.JsonParser;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleResponse;
import io.castle.client.model.generated.*;
-import io.castle.client.utils.DeviceUtils;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
import org.assertj.core.api.Assertions;
@@ -20,10 +17,6 @@
public class CastleLogHttpTest extends AbstractCastleHttpLayerTest {
- public CastleLogHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
@Test public void log() throws InterruptedException {
MockResponse mockResponse = new MockResponse();
mockResponse.setResponseCode(204);
diff --git a/src/test/java/io/castle/client/CastleMergeHttpTest.java b/src/test/java/io/castle/client/CastleMergeHttpTest.java
deleted file mode 100644
index 8323ad55..00000000
--- a/src/test/java/io/castle/client/CastleMergeHttpTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
-import io.castle.client.utils.SDKVersion;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Assert;
-import org.junit.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import com.google.common.collect.ImmutableMap;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.concurrent.atomic.AtomicReference;
-import io.castle.client.model.AsyncCallbackHandler;
-import org.assertj.core.api.Assertions;
-
-public class CastleMergeHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleMergeHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test
- public void mergeContextIsSendOnHttp() throws InterruptedException, JSONException {
- // Given
- server.enqueue(new MockResponse().setResponseCode(200));
- String event = "any.valid.event";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And a extra context
- CustomExtraContext customExtraContext = new CustomExtraContext();
- customExtraContext.setAddString("String value");
- customExtraContext.setCondition(true);
- customExtraContext.setValue(10L);
- sdk.onRequest(request).mergeContext(customExtraContext).track(event, null, null, null, ImmutableMap.builder()
- .put("x", "valueX")
- .put("y", 234567)
- .build());
-
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
-
- JSONAssert.assertEquals("{\"event\":\"any.valid.event\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +",\"add_string\":\"String value\",\"condition\":true,\"value\":10},\"user_traits\":{\"x\":\"valueX\",\"y\":234567}}", body, false);
- }
-
- @Test
- public void mergeContextIsNull() throws InterruptedException, JSONException {
- // Given
- server.enqueue(new MockResponse().setResponseCode(200));
- String event = "any.valid.event";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And null context
- sdk.onRequest(request).mergeContext(null).track(event, null, null, null);
-
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
-
- JSONAssert.assertEquals("{\"event\":\"any.valid.event\"}", body, false);
- }
-
- private static class CustomExtraContext {
- private String addString;
- private Boolean condition;
- private Long value;
-
- public String getAddString() {
- return addString;
- }
-
- public void setAddString(String addString) {
- this.addString = addString;
- }
-
- public Boolean getCondition() {
- return condition;
- }
-
- public void setCondition(Boolean condition) {
- this.condition = condition;
- }
-
- public Long getValue() {
- return value;
- }
-
- public void setValue(Long value) {
- this.value = value;
- }
- }
-}
diff --git a/src/test/java/io/castle/client/CastleNoTrackOptionTest.java b/src/test/java/io/castle/client/CastleNoTrackOptionTest.java
deleted file mode 100644
index 144a03f7..00000000
--- a/src/test/java/io/castle/client/CastleNoTrackOptionTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.api.CastleApi;
-import io.castle.client.model.AsyncCallbackHandler;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
-import io.castle.client.model.Verdict;
-import okhttp3.mockwebserver.MockResponse;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class CastleNoTrackOptionTest extends AbstractCastleHttpLayerTest {
-
- public CastleNoTrackOptionTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test
- public void noTrackOptionDisableRequests() throws InterruptedException {
-
- //Given a mock HTTP request is provided
- HttpServletRequest request = new MockHttpServletRequest();
- //And a CastleApi is created with doNotTrack option
- CastleApi castleApi = sdk.onRequest(request).doNotTrack(true);
- // And a custom handler for the async authenticate call
- final AtomicReference authenticateAsyncResult = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- authenticateAsyncResult.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
-
- //When all API call are executed
- castleApi.track("testEvent");
- Verdict verdict = castleApi.authenticate("testEvent", "userId");
- castleApi.authenticateAsync("testEvent", "userId", handler);
-
- //and we await the timeout period to avoid concurrency races
- Thread.sleep(120);
-
- //Then no calls are made to the backend
- Assertions.assertThat(server.getRequestCount()).isEqualTo(0);
- //And the Verdict is the default ALLOW value
- Assert.assertEquals(AuthenticateAction.ALLOW, verdict.getAction());
- Assert.assertEquals("userId", verdict.getUserId());
- Assert.assertEquals(AuthenticateAction.ALLOW, authenticateAsyncResult.get().getAction());
- Assertions.assertThat(authenticateAsyncResult.get().isFailover()).isTrue();
- }
-
- @Test
- public void useCustomTrackHandlerWithNoTrackOptionToDisableRequests() {
- //Given a mock HTTP request is provided
- HttpServletRequest request = new MockHttpServletRequest();
- //And a CastleApi is created with doNotTrack option
- CastleApi castleApi = sdk.onRequest(request).doNotTrack(true);
- // And a custom handler for the async track call
- final AtomicReference trackAsyncResult = new AtomicReference<>();
-
- AsyncCallbackHandler trackHandler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Boolean response) {
- trackAsyncResult.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
-
- }
- };
-
- //When an API call is executed
- castleApi.track(
- "testEvent",
- null,
- null,
- null,
- null,
- trackHandler
- );
-
- //Then no calls are made to the backend
- Assertions.assertThat(server.getRequestCount()).isEqualTo(0);
-
- //And the track call returns and the resulting boolean is true
- Assert.assertTrue(trackAsyncResult.get());
- }
-
- @Test
- public void noTrackOptionCreateAllowFailoverVerdict() {
-
- //Given a mock HTTP request is provided
- HttpServletRequest request = new MockHttpServletRequest();
- //And a CastleApi is created with doNotTrack option
- CastleApi castleApi = sdk.onRequest(request).doNotTrack(true);
- //When authenticate is called
-
- Verdict verdict = castleApi.authenticate("testEvent", "userId");
-
- //Then no calls are made to the backend
- Assertions.assertThat(server.getRequestCount()).isEqualTo(0);
- //And the Verdict is the correct failover value
- Assert.assertEquals(AuthenticateAction.ALLOW, verdict.getAction());
-
- Assertions.assertThat(verdict.getUserId()).isEqualTo("userId");
- Assertions.assertThat(verdict.getAction()).isEqualTo(AuthenticateAction.ALLOW);
- Assertions.assertThat(verdict.getFailoverReason()).isEqualTo("Castle set to do not track.");
- Assertions.assertThat(verdict.isFailover()).isTrue();
- }
-
-
- @Test
- public void trackOptionAllowRequests() throws InterruptedException {
-
- //Given a mock HTTP request is provided
- HttpServletRequest request = new MockHttpServletRequest();
- //And responses are setup on mock server
- server.enqueue(new MockResponse().setResponseCode(200));
- server.enqueue(new MockResponse().setBody(
- "{\n" +
- " \"action\": \"deny\",\n" +
- " \"user_id\": \"12345\"\n" +
- "}"));
- server.enqueue(new MockResponse().setBody(
- "{\n" +
- " \"action\": \"deny\",\n" +
- " \"user_id\": \"12345\"\n" +
- "}"));
- //And a CastleApi is created with doNotTrack option
- CastleApi castleApi = sdk.onRequest(request).doNotTrack(false);
- // And a custom handler for the async authenticate call
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler handler = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("error on request", exception);
- }
- };
-
- //When all API call are executed
- castleApi.track("testEvent");
- server.takeRequest();
- castleApi.authenticate("testEvent", "userId");
- server.takeRequest();
- castleApi.authenticateAsync("testEvent", "userId", handler);
- server.takeRequest();
-
- //Then all the calls are executed
- waitForValue(result);
- Assertions.assertThat(server.getRequestCount()).isEqualTo(3);
- Assert.assertEquals(AuthenticateAction.DENY, result.get().getAction());
- }
-
-}
diff --git a/src/test/java/io/castle/client/CastlePrivacyHttpTest.java b/src/test/java/io/castle/client/CastlePrivacyHttpTest.java
new file mode 100644
index 00000000..59cf2c16
--- /dev/null
+++ b/src/test/java/io/castle/client/CastlePrivacyHttpTest.java
@@ -0,0 +1,57 @@
+package io.castle.client;
+
+import com.google.common.collect.ImmutableMap;
+import io.castle.client.model.CastleResponse;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.assertj.core.api.Assertions;
+import org.json.JSONException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+public class CastlePrivacyHttpTest extends AbstractCastleHttpLayerTest {
+
+ @Test
+ public void requestUserData() throws InterruptedException, JSONException {
+ server.enqueue(new MockResponse().setBody("{}"));
+ HttpServletRequest request = new MockHttpServletRequest();
+
+ CastleResponse response = sdk.onRequest(request).requestUserData(ImmutableMap.builder()
+ .put("identifier", "12345")
+ .put("identifier_type", "$id")
+ .build());
+ if (response == null) {
+ Assertions.fail("error on request");
+ }
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ String body = recordedRequest.getBody().readUtf8();
+ Assert.assertEquals(testServerBaseUrl.resolve("v1/privacy/users"), recordedRequest.getRequestUrl());
+ Assert.assertEquals("POST", recordedRequest.getMethod());
+ JSONAssert.assertEquals("{\"identifier\":\"12345\",\"identifier_type\":\"$id\"}", body, false);
+ }
+
+ @Test
+ public void deleteUserData() throws InterruptedException, JSONException {
+ server.enqueue(new MockResponse().setBody("{}"));
+ HttpServletRequest request = new MockHttpServletRequest();
+
+ CastleResponse response = sdk.onRequest(request).deleteUserData(ImmutableMap.builder()
+ .put("identifier", "12345")
+ .put("identifier_type", "$id")
+ .build());
+ if (response == null) {
+ Assertions.fail("error on request");
+ }
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ String body = recordedRequest.getBody().readUtf8();
+ Assert.assertEquals(testServerBaseUrl.resolve("v1/privacy/users"), recordedRequest.getRequestUrl());
+ Assert.assertEquals("DELETE", recordedRequest.getMethod());
+ JSONAssert.assertEquals("{\"identifier\":\"12345\",\"identifier_type\":\"$id\"}", body, false);
+ }
+}
diff --git a/src/test/java/io/castle/client/CastleRecoverHttpTest.java b/src/test/java/io/castle/client/CastleRecoverHttpTest.java
deleted file mode 100644
index cdc76bd0..00000000
--- a/src/test/java/io/castle/client/CastleRecoverHttpTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.*;
-import io.castle.client.utils.DeviceUtils;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleRecoverHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleRecoverHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void recoverDevices() throws InterruptedException {
- MockResponse mockResponse = new MockResponse();
- mockResponse.setBody(DeviceUtils.getDefaultDeviceJSON());
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an report device request is made
- CastleResponse response = sdk.onRequest(request).recover("1234");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/users/1234/recover"), recordedRequest.getRequestUrl());
- Assert.assertEquals("PUT", recordedRequest.getMethod());
- }
-}
diff --git a/src/test/java/io/castle/client/CastleRemoveUserHttpTest.java b/src/test/java/io/castle/client/CastleRemoveUserHttpTest.java
deleted file mode 100644
index 06233e8d..00000000
--- a/src/test/java/io/castle/client/CastleRemoveUserHttpTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
-import io.castle.client.model.CastleApiInternalServerErrorException;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.junit.Assert;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-public class CastleRemoveUserHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleRemoveUserHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test
- public void removeUser() throws Exception {
- // Mock a response
- MockResponse mockResponse = new MockResponse();
- mockResponse.setResponseCode(202);
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an privacy remove user request is made
- Boolean response = sdk.onRequest(request).removeUser("user-test-id");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/privacy/users/user-test-id"), recordedRequest.getRequestUrl());
- Assert.assertEquals("DELETE", recordedRequest.getMethod());
- Assert.assertTrue(response);
- }
-
- @Test
- public void removeUserNotFoundTest() throws Exception {
- // Mock a response
- MockResponse mockResponse = new MockResponse();
- mockResponse.setResponseCode(404);
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an privacy remove user request is made
- Boolean response = sdk.onRequest(request).removeUser("user-test-id");
-
- // Then it doesn't throw exception
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals(testServerBaseUrl.resolve("v1/privacy/users/user-test-id"), recordedRequest.getRequestUrl());
- Assert.assertEquals("DELETE", recordedRequest.getMethod());
-
- Assert.assertNull(response);
- }
-
- @Test(expected = CastleApiInternalServerErrorException.class)
- public void removeUserServerError() {
- // Mock a response
- MockResponse mockResponse = new MockResponse();
- mockResponse.setResponseCode(500);
- server.enqueue(mockResponse);
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And a privacy remove user request is made, exception throw
- Boolean response = sdk.onRequest(request).removeUser("user-test-id");
-
- Assert.assertNull(response);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/io/castle/client/CastleRiskHttpTest.java b/src/test/java/io/castle/client/CastleRiskHttpTest.java
index a3bb7a26..cae714b0 100644
--- a/src/test/java/io/castle/client/CastleRiskHttpTest.java
+++ b/src/test/java/io/castle/client/CastleRiskHttpTest.java
@@ -2,8 +2,6 @@
import com.google.gson.JsonParser;
import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.generated.*;
import jakarta.servlet.http.HttpServletRequest;
import okhttp3.mockwebserver.MockResponse;
@@ -18,10 +16,6 @@
public class CastleRiskHttpTest extends AbstractCastleHttpLayerTest {
- public CastleRiskHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
@Test public void risk() throws InterruptedException {
MockResponse mockResponse = new MockResponse();
mockResponse.setBody("{\n" +
@@ -229,7 +223,7 @@ public CastleRiskHttpTest() {
Assert.assertEquals("America/New_York", response.getDevice().getTimezone().getName());
Assert.assertEquals(-300, response.getDevice().getTimezone().getOffset());
- Assert.assertEquals(2, response.getDevice().getScreen().getScreen());
+ Assert.assertEquals(Integer.valueOf(2), response.getDevice().getScreen().getScreen());
Assert.assertEquals("landscape", response.getDevice().getScreen().getOrientation());
Assert.assertEquals("ASZoelALT5-PaVw2pAVMXg", response.getId());
@@ -433,4 +427,15 @@ public void compareRiskJson() {
// Compare the JSON strings
Assert.assertEquals(JsonParser.parseString(providedJson), JsonParser.parseString(riskJson));
}
+
+ @Test
+ public void expandedEventAllowsMissingOptionalIntegers() {
+ FilterAndRiskResponse response = new CastleGsonModel().getGson().fromJson(
+ "{\"type\":\"$login\",\"status\":\"$succeeded\",\"id\":\"evt_1\","
+ + "\"device\":{\"screen\":{}},\"ip\":{}}",
+ FilterAndRiskResponse.class);
+
+ Assert.assertNull(response.getDevice().getScreen().getScreen());
+ Assert.assertNull(response.getIp().getAsn());
+ }
}
diff --git a/src/test/java/io/castle/client/CastleTest.java b/src/test/java/io/castle/client/CastleTest.java
index 4fd5a9cf..e02b8484 100644
--- a/src/test/java/io/castle/client/CastleTest.java
+++ b/src/test/java/io/castle/client/CastleTest.java
@@ -1,11 +1,9 @@
package io.castle.client;
-import io.castle.client.internal.backend.RestApiFactory;
import io.castle.client.internal.config.CastleConfiguration;
import io.castle.client.model.CastleSdkConfigurationException;
import org.assertj.core.api.Assertions;
import org.junit.Test;
-import org.mockito.Mockito;
public class CastleTest {
@@ -82,6 +80,7 @@ public void sdkProvideASingleton() throws CastleSdkConfigurationException {
//Then the same singleton instance is returned
Assertions.assertThat(sdk1).isSameAs(sdk2);
+ sdk.close();
}
@Test
@@ -125,5 +124,35 @@ public void sdkOnConfigureWithSecretOnly() throws CastleSdkConfigurationExceptio
Assertions.assertThat(sdkConfiguration)
.extracting("apiSecret")
.isEqualTo("abcd");
+ sdk.close();
+ }
+
+ @Test
+ public void closeReleasesHttpClient() throws CastleSdkConfigurationException {
+ Castle sdk = Castle.initialize("abcd");
+ sdk.close();
+ }
+
+ @Test
+ public void closeClearsTheSingletonWhenThisInstanceIsTheSingleton() throws CastleSdkConfigurationException {
+ Castle sdk = Castle.verifySdkConfigurationAndInitialize();
+ Castle.setSingletonInstance(sdk);
+
+ sdk.close();
+
+ Assertions.assertThatThrownBy(Castle::instance)
+ .isInstanceOf(IllegalStateException.class);
+ }
+
+ @Test
+ public void closeDoesNotClearADifferentSingleton() throws CastleSdkConfigurationException {
+ Castle singleton = Castle.verifySdkConfigurationAndInitialize();
+ Castle.setSingletonInstance(singleton);
+ Castle other = Castle.initialize("abcd");
+
+ other.close();
+
+ Assertions.assertThat(Castle.instance()).isSameAs(singleton);
+ singleton.close();
}
}
diff --git a/src/test/java/io/castle/client/CastleThrowStrategyTest.java b/src/test/java/io/castle/client/CastleThrowStrategyTest.java
deleted file mode 100644
index c5c7df0e..00000000
--- a/src/test/java/io/castle/client/CastleThrowStrategyTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.*;
-import okhttp3.mockwebserver.MockResponse;
-import org.assertj.core.api.Assertions;
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static okhttp3.mockwebserver.SocketPolicy.NO_RESPONSE;
-
-public class CastleThrowStrategyTest extends AbstractCastleHttpLayerTest {
-
- public CastleThrowStrategyTest() {
- super(new AuthenticateFailoverStrategy());
- }
-
- @Test(expected = CastleApiTimeoutException.class)
- public void onThrowStrategyAuthenticateThrowCastleRuntimeExceptionWhenNoResponse() {
- // given the throw strategy is setup (see constructor)
- // and backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- // and
- String id = "12345";
- String event = "$login.succeeded";
-
- // and a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // when authenticate request is made
- sdk.onRequest(request).authenticate(event, id);
-
- // then the exception is throw
- }
-
- @Test(expected = CastleApiInternalServerErrorException.class)
- public void onThrowStrategyAuthenticateThrowCastleRuntimeExceptionWhenResponseIs500() {
- // given the throw strategy is setup (see constructor)
- // and backend request timeouts
- server.enqueue(new MockResponse().setResponseCode(500));
- // and
- String id = "12345";
- String event = "$login.succeeded";
-
- // and a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // when authenticate request is made
- Verdict authenticate = sdk.onRequest(request).authenticate(event, id);
-
- // then the exception is throw
- Assertions.fail("A exception should be throw");
- }
-
- @Test
- public void onThrowStrategyAuthenticateAsyncThrowCastleRuntimeException() {
- // given the throw strategy is setup (see constructor)
- // and backend request timeouts
- server.enqueue(new MockResponse().setSocketPolicy(NO_RESPONSE));
- // and
- String id = "12345";
- String event = "$login.succeeded";
-
- // and a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
- // and a async callback is prepared
- final AtomicReference exceptionAtomicReference = new AtomicReference<>();
- AsyncCallbackHandler callback = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Verdict response) {
- Assertions.fail("A exceptions was expected");
- }
-
- @Override
- public void onException(Exception exception) {
- exceptionAtomicReference.set(exception);
- }
- };
- // when authenticate request is made
- sdk.onRequest(request).authenticateAsync(event, id, callback);
-
- // then the exception is throw in the callback
- Exception exception = waitForValue(exceptionAtomicReference);
- Assertions.assertThat(exception)
- .isNotNull()
- .isInstanceOf(CastleRuntimeException.class);
- }
-
-
-}
diff --git a/src/test/java/io/castle/client/CastleTrackHttpTest.java b/src/test/java/io/castle/client/CastleTrackHttpTest.java
deleted file mode 100644
index 9fb036db..00000000
--- a/src/test/java/io/castle/client/CastleTrackHttpTest.java
+++ /dev/null
@@ -1,313 +0,0 @@
-package io.castle.client;
-
-import io.castle.client.model.AsyncCallbackHandler;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
-import io.castle.client.model.CastleContext;
-import io.castle.client.model.CastleHeaders;
-import io.castle.client.utils.SDKVersion;
-import io.castle.client.model.CastleMessage;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.RecordedRequest;
-import okhttp3.mockwebserver.SocketPolicy;
-import org.assertj.core.api.Assertions;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.Assert;
-import org.junit.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import com.google.common.collect.ImmutableMap;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class CastleTrackHttpTest extends AbstractCastleHttpLayerTest {
-
- public CastleTrackHttpTest() {
- super(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE));
- }
-
- @Test public void trackWithManualContext() throws Exception {
- server.enqueue(new MockResponse());
-
-
- // When
- CastleContext context = sdk.contextBuilder()
- .ip("1.1.1.1")
- .userAgent("Mozilla/5.0")
- .clientId("")
- .headers(CastleHeaders.builder()
- .add("User-Agent", "Mozilla/5.0")
- .add("Accept-Language", "sv-se")
- .build()
- )
- .build();
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And a track request is made
- sdk.onRequest(request).track(CastleMessage.builder("$login.succeeded")
- .userId("12345")
- .context(context)
- .build()
- );
-
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"client_id\":\"\",\"ip\":\"1.1.1.1\",\"headers\":{\"User-Agent\":\"Mozilla/5.0\",\"Accept-Language\":\"sv-se\"}," + SDKVersion.getLibraryString() +",\"user_agent\":\"Mozilla/5.0\"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointHttpAuthorize() throws Exception {
- // Given
- Castle.verifySdkConfigurationAndInitialize();
-
- server.enqueue(new MockResponse());
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // And an track request is made
- sdk.onRequest(request).track("$login.successful", "1234");
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- Assert.assertEquals("Basic OnRlc3RfYXBpX3NlY3JldA==", recordedRequest.getHeader("Authorization"));
- }
-
- @Test
- public void trackEndpointWithPaylod() throws InterruptedException, JSONException {
- // Given
- server.enqueue(new MockResponse());
- CastleMessage payload = CastleMessage.builder("$login.succeeded")
- .userId("12345")
- .build();
- HttpServletRequest request = new MockHttpServletRequest();
-
- // When
- sdk.onRequest(request).track(payload);
-
- // Then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointWithUserIDTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse());
- String id = "12345";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an track request is made
- sdk.onRequest(request).track(event, id);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointWithUserIDAndReviewIdTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse());
- String userId = "12345";
- String reviewId = "r45677";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an track request is made
- sdk.onRequest(request).track(event, userId,reviewId);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"review_id\":\"r45677\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointWithUserIDAndPropertiesTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse());
- String userId = "12345";
- String reviewId = "r987";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an track request is made
- sdk.onRequest(request).track(event, userId, reviewId, ImmutableMap.builder()
- .put("a","valueA")
- .put("b",123456)
- .build());
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"properties\":{\"a\":\"valueA\",\"b\":123456},\"review_id\":\"r987\",\"user_id\":\"12345\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointWithUserIDAndPropertiesAndTraitTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse());
- String userId = "23456";
- String reviewId = "r987";
- String event = "$login.succeeded";
-
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an track request is made
- sdk.onRequest(request).track(event, userId, reviewId, ImmutableMap.builder()
- .put("a","valueA")
- .put("b",123456)
- .build(), ImmutableMap.builder()
- .put("x", "x value")
- .put("y", 2342)
- .build());
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"$login.succeeded\",\"properties\":{\"a\":\"valueA\",\"b\":123456},\"review_id\":\"r987\",\"user_id\":\"23456\",\"user_traits\":{\"x\":\"x value\",\"y\":2342},\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointWithMinimalInformationTest() throws InterruptedException, JSONException {
- //given
- server.enqueue(new MockResponse());
- String event = "any.valid.event";
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an track request is made
- sdk.onRequest(request).track(event);
-
- // then
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"any.valid.event\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
- }
-
- @Test
- public void trackEndpointAsyncTest() throws InterruptedException {
- //given
- server.enqueue(new MockResponse().setResponseCode(200));
- String event = "any.valid.event";
- // And a mock Request
- HttpServletRequest request = new MockHttpServletRequest();
-
- // and an async track request is made
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler callback = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Boolean response) {
- result.set(response);
- }
-
- @Override
- public void onException(Exception exception) {
- Assertions.fail("No exception expected here");
- }
- };
- sdk.onRequest(request).track(event, null, null, null,null,callback);
-
- // then the callback response is called with true
- Boolean extracted = waitForValue(result);
- Assertions.assertThat(extracted).isTrue();
- }
-
- @Test
- public void trackEndpointTimeoutTest() throws InterruptedException, JSONException {
- // given the backend will timeout
- server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE));
- String event = "any.valid.event";
- // and a mock Request
- final HttpServletRequest request = new MockHttpServletRequest();
-
- // and a async handler is prepared
- final AtomicReference result = new AtomicReference<>();
- AsyncCallbackHandler callback = new AsyncCallbackHandler() {
- @Override
- public void onResponse(Boolean response) {
- Assertions.fail("Request should end with an timeout exception and not result");
- }
-
- @Override
- public void onException(Exception exception) {
- result.set(false);
- }
- };
- // when an track request is made
- sdk.onRequest(request).track(event, null, null, null,null,callback);
-
- // then the track request must be send
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"any.valid.event\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
-
- // and the onException method must be called
- waitForValueAndVerify(result, Boolean.FALSE);
- }
-
- @Test
- public void trackEndpointTimeoutAreIgnoreWhenNoCallbackIsProvidedTest() throws InterruptedException, JSONException {
- // given the backend will timeout
- server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE));
- String event = "any.valid.event";
- // and a mock Request
- final HttpServletRequest request = new MockHttpServletRequest();
-
- // when an track request is made
- sdk.onRequest(request).track(event, null, null, null);
-
- // then the track request must be send
- RecordedRequest recordedRequest = server.takeRequest();
- String body = recordedRequest.getBody().readUtf8();
- JSONAssert.assertEquals("{\"event\":\"any.valid.event\",\"context\":{\"active\":true,\"ip\":\"127.0.0.1\",\"headers\":{\"REMOTE_ADDR\":\"127.0.0.1\"}," + SDKVersion.getLibraryString() +"}}",
- body, false);
-
- Assert.assertTrue(new JSONObject(body).has("sent_at"));
-
- // and no exceptions are thrown in any thread
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/io/castle/client/CastleWebhookTest.java b/src/test/java/io/castle/client/CastleWebhookTest.java
new file mode 100644
index 00000000..c6478a97
--- /dev/null
+++ b/src/test/java/io/castle/client/CastleWebhookTest.java
@@ -0,0 +1,38 @@
+package io.castle.client;
+
+import io.castle.client.internal.utils.Webhook;
+import io.castle.client.model.CastleSdkConfigurationException;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+public class CastleWebhookTest {
+
+ private static final String SECRET = "test_api_secret";
+
+ @Test
+ public void verifiesValidSignature() throws CastleSdkConfigurationException {
+ Castle sdk = Castle.verifySdkConfigurationAndInitialize();
+ byte[] body = "{\"type\":\"$review.opened\"}".getBytes(StandardCharsets.UTF_8);
+ String signature = Webhook.computeSignature(SECRET, body);
+
+ Assertions.assertThat(sdk.verifyWebhookSignature(signature, body)).isTrue();
+ }
+
+ @Test
+ public void rejectsInvalidSignature() throws CastleSdkConfigurationException {
+ Castle sdk = Castle.verifySdkConfigurationAndInitialize();
+ byte[] body = "{\"type\":\"$review.opened\"}".getBytes(StandardCharsets.UTF_8);
+
+ Assertions.assertThat(sdk.verifyWebhookSignature("not-the-signature", body)).isFalse();
+ }
+
+ @Test
+ public void rejectsNullSignature() throws CastleSdkConfigurationException {
+ Castle sdk = Castle.verifySdkConfigurationAndInitialize();
+ byte[] body = "{}".getBytes(StandardCharsets.UTF_8);
+
+ Assertions.assertThat(sdk.verifyWebhookSignature((String) null, body)).isFalse();
+ }
+}
diff --git a/src/test/java/io/castle/client/internal/config/ConfigurationLoaderTest.java b/src/test/java/io/castle/client/internal/config/ConfigurationLoaderTest.java
index 25d0ac7d..490082d6 100644
--- a/src/test/java/io/castle/client/internal/config/ConfigurationLoaderTest.java
+++ b/src/test/java/io/castle/client/internal/config/ConfigurationLoaderTest.java
@@ -1,7 +1,5 @@
package io.castle.client.internal.config;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.AuthenticateFailoverStrategy;
import io.castle.client.model.CastleRuntimeException;
import io.castle.client.model.CastleSdkConfigurationException;
import org.assertj.core.api.Assertions;
@@ -88,7 +86,6 @@ public void loadFromProperties() throws CastleSdkConfigurationException {
)
.withDefaultBackendProvider()
.withTimeout(100)
- .withAuthenticateFailoverStrategy(new AuthenticateFailoverStrategy(AuthenticateAction.CHALLENGE))
.build();
//Then the value of the timeout should be the one in the properties file castle_sdk.properties
@@ -105,10 +102,6 @@ public void loadFromEnv() throws CastleSdkConfigurationException {
"CASTLE_SDK_TIMEOUT",
"700"
);
- setEnvAndTestCorrectness(
- "CASTLE_SDK_AUTHENTICATE_FAILOVER_STRATEGY",
- "DENY"
- );
setEnvAndTestCorrectness(
"CASTLE_SDK_ALLOWLIST_HEADERS",
"Accept-Encoding,Accept-Charset"
@@ -145,7 +138,6 @@ public void loadFromEnv() throws CastleSdkConfigurationException {
.withApiBaseUrl("https://api.dev.castle.io/v1/")
.withTimeout(700)
.withLogHttpRequests(true)
- .withAuthenticateFailoverStrategy(new AuthenticateFailoverStrategy(AuthenticateAction.DENY))
.build();
testLoad(expectedConfiguration);
@@ -160,7 +152,6 @@ public void loadLegacyApiSecretEnv() throws CastleSdkConfigurationException {
"CASTLE_SDK_API_SECRET",
"1234"
);
- // and a expected config is the default configuration with the throw strategy
CastleConfiguration expectedConfiguration = CastleConfigurationBuilder
.defaultConfigBuilder()
.withApiSecret("1234")
@@ -170,37 +161,6 @@ public void loadLegacyApiSecretEnv() throws CastleSdkConfigurationException {
testLoad(expectedConfiguration);
}
- @Test
- public void loadFailoverThrowStrategyFromEnv() throws CastleSdkConfigurationException {
- //given a property file not existing is provided
- setEnvAndTestCorrectness("CASTLE_PROPERTIES_FILE", "notExistingFile.properties");
- // and a minimal sdk configuration is provided in environment
- setEnvAndTestCorrectness(
- "CASTLE_SDK_APP_ID",
- "test_app_id_env"
- );
- setEnvAndTestCorrectness(
- "CASTLE_SDK_API_SECRET",
- "1234"
- );
- // and the failover strategy environment value is throw
- setEnvAndTestCorrectness(
- "CASTLE_SDK_AUTHENTICATE_FAILOVER_STRATEGY",
- "throw"
- );
- // and an expected config is the default configuration with the throw strategy
- CastleConfiguration expectedConfiguration = CastleConfigurationBuilder
- .defaultConfigBuilder()
- .withApiSecret("1234")
- .withCastleAppId("test_app_id_env")
- .withAuthenticateFailoverStrategy(new AuthenticateFailoverStrategy())
- .build();
-
- // when then
- testLoad(expectedConfiguration);
- }
-
-
@Test
public void ignoreNonExistingPropertiesFileTest() throws CastleSdkConfigurationException {
//given a property file not existing is provided
diff --git a/src/test/java/io/castle/client/internal/utils/VerdictTransportModelTest.java b/src/test/java/io/castle/client/internal/utils/VerdictTransportModelTest.java
deleted file mode 100644
index 057c1279..00000000
--- a/src/test/java/io/castle/client/internal/utils/VerdictTransportModelTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package io.castle.client.internal.utils;
-
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.model.AuthenticateAction;
-import io.castle.client.model.RiskPolicyType;
-import org.assertj.core.api.Assertions;
-import org.junit.Test;
-
-public class VerdictTransportModelTest {
-
- @Test
- public void transportProtocolIsWellDeserialized() {
- // given a json input value
- String json = "{\n" +
- " \"action\": \"deny\",\n" +
- " \"user_id\": \"12345\",\n" +
- " \"device_token\": \"abcdefg1234\",\n" +
- " \"risk_policy\": {\n" +
- " \"id\": \"q-rbeMzBTdW2Fd09sbz55A\",\n" +
- " \"revision_id\": \"pke4zqO2TnqVr-NHJOAHEg\",\n" +
- " \"name\": \"Block Users from X\",\n" +
- " \"type\": \"bot\"\n" +
- " }\n" +
- "}";
- // and castle gson model
- CastleGsonModel model = new CastleGsonModel();
-
-
- // when the Verdict transport object is deserialized
- VerdictTransportModel loaded = model.getGson().fromJson(json, VerdictTransportModel.class);
-
- Assertions.assertThat(loaded.getAction()).isEqualTo(AuthenticateAction.DENY);
- Assertions.assertThat(loaded.getDeviceToken()).isEqualTo("abcdefg1234");
- Assertions.assertThat(loaded.getUserId()).isEqualTo("12345");
- Assertions.assertThat(loaded.getRiskPolicy().getId()).isEqualTo("q-rbeMzBTdW2Fd09sbz55A");
- Assertions.assertThat(loaded.getRiskPolicy().getRevisionId()).isEqualTo("pke4zqO2TnqVr-NHJOAHEg");
- Assertions.assertThat(loaded.getRiskPolicy().getName()).isEqualTo("Block Users from X");
- Assertions.assertThat(loaded.getRiskPolicy().getType()).isEqualTo(RiskPolicyType.BOT);
- }
-}
diff --git a/src/test/java/io/castle/client/model/AuthenticationActionTest.java b/src/test/java/io/castle/client/model/AuthenticationActionTest.java
deleted file mode 100644
index 57c1e5d0..00000000
--- a/src/test/java/io/castle/client/model/AuthenticationActionTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.castle.client.model;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class AuthenticationActionTest {
-
- @Test
- public void testFromAllow() {
- testFromActionMethod("AllOw", AuthenticateAction.ALLOW);
- }
-
- @Test
- public void testFromDeny() {
- testFromActionMethod("Deny", AuthenticateAction.DENY);
- }
-
- @Test
- public void testFromChallenge() {
- testFromActionMethod("cHallenge", AuthenticateAction.CHALLENGE);
- }
-
- @Test
- public void testFromNull() {
- testFromActionMethod(null, null);
- }
-
- @Test
- public void testFromEmptyString() {
- testFromActionMethod("", null);
- }
-
- @Test
- public void testFromWrongString() {
- testFromActionMethod("getMeANull", null);
- }
-
- private void testFromActionMethod(String from, AuthenticateAction expected) {
-
- AuthenticateAction authenticateAction = AuthenticateAction.fromAction(from);
- Assert.assertEquals(expected, authenticateAction);
-
- }
-
-
-}
diff --git a/src/test/java/io/castle/client/model/CastleConfigurationBuilderTest.java b/src/test/java/io/castle/client/model/CastleConfigurationBuilderTest.java
index 7805885c..a5eddd87 100644
--- a/src/test/java/io/castle/client/model/CastleConfigurationBuilderTest.java
+++ b/src/test/java/io/castle/client/model/CastleConfigurationBuilderTest.java
@@ -5,6 +5,8 @@
import org.assertj.core.api.Assertions;
import org.junit.Test;
+import java.time.Duration;
+
public class CastleConfigurationBuilderTest {
@Test
@@ -23,10 +25,20 @@ public void buildDefaultConfiguration() throws CastleSdkConfigurationException {
Assertions.assertThat(config.getDenyListHeaders()).contains("cookie");
Assertions.assertThat(config.getDenyListHeaders()).contains("authorization");
Assertions.assertThat(config.getAllowListHeaders()).isEmpty();
- Assertions.assertThat(config.getAuthenticateFailoverStrategy().getDefaultAction()).isEqualTo(AuthenticateAction.ALLOW);
- Assertions.assertThat(config.getTimeout()).isEqualTo(500);
+ Assertions.assertThat(config.getTimeout()).isEqualTo(1000);
+
+ }
+
+ @Test
+ public void buildConfigurationWithDurationTimeout() throws CastleSdkConfigurationException {
+ CastleConfiguration config = CastleConfigurationBuilder.defaultConfigBuilder()
+ .withApiSecret("TestApiSecret")
+ .withCastleAppId("TestCastleAppId")
+ .withTimeout(Duration.ofSeconds(2))
+ .build();
+ Assertions.assertThat(config.getTimeout()).isEqualTo(2000);
}
@Test(expected = CastleSdkConfigurationException.class)
diff --git a/src/test/java/io/castle/client/model/CastleMessageTest.java b/src/test/java/io/castle/client/model/CastleMessageTest.java
deleted file mode 100644
index 1cbbce0e..00000000
--- a/src/test/java/io/castle/client/model/CastleMessageTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package io.castle.client.model;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import io.castle.client.internal.json.CastleGsonModel;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.HashMap;
-
-public class CastleMessageTest {
-
- private CastleGsonModel model = new CastleGsonModel();
-
- @Test
- public void jsonSerialized() {
- // Given
- CastleMessage message = new CastleMessage("event");
- message.setCreatedAt("2018-01-01");
- message.setTimestamp("2018-01-01");
- message.setDeviceToken("1234");
- message.setReviewId("2345");
- message.setProperties(ImmutableMap.builder()
- .put("key", "val")
- .build());
- message.setUserId("3456");
- message.setUserTraits(ImmutableMap.builder()
- .put("key", "val")
- .build());
-
- // When
- String payloadJson = model.getGson().toJson(message);
- String expected = "{\"created_at\":\"2018-01-01\",\"timestamp\":\"2018-01-01\",\"device_token\":\"1234\",\"event\":\"event\",\"properties\":{\"key\":\"val\"},\"review_id\":\"2345\",\"user_id\":\"3456\",\"user_traits\":{\"key\":\"val\"}}";
-
- // Then
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
-
- }
-
- @Test
- public void fullBuilderJson() {
- // Given
- CastleMessage message = CastleMessage.builder("event")
- .createdAt("2018-01-01")
- .timestamp("2018-01-01")
- .deviceToken("1234")
- .reviewId("2345")
- .properties(ImmutableMap.builder()
- .put("key", "val")
- .build())
- .userId("3456")
- .userTraits(ImmutableMap.builder()
- .put("key", "val")
- .build())
- .build();
-
- Assert.assertEquals(message.getCreatedAt(), "2018-01-01");
- Assert.assertEquals(message.getDeviceToken(), "1234");
- Assert.assertEquals(message.getReviewId(), "2345");
- Assert.assertEquals(message.getUserId(), "3456");
- Assert.assertEquals(message.getEvent(), "event");
- Assert.assertEquals(message.getUserTraits(), ImmutableMap.builder()
- .put("key", "val")
- .build());
- Assert.assertEquals(message.getProperties(), ImmutableMap.builder()
- .put("key", "val")
- .build());
-
- // When
- String payloadJson = model.getGson().toJson(message);
- String expected = "{\"created_at\":\"2018-01-01\",\"timestamp\":\"2018-01-01\",\"device_token\":\"1234\",\"event\":\"event\",\"properties\":{\"key\":\"val\"},\"review_id\":\"2345\",\"user_id\":\"3456\",\"user_traits\":{\"key\":\"val\"}}";
-
- // Then
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
- }
-
- @Test
- public void properties() {
- JsonObject jsonObject = new JsonObject();
- jsonObject.addProperty("key", "value");
-
- CastleMessage message = CastleMessage.builder("event")
- .properties(jsonObject)
- .build();
-
- String payloadJson = model.getGson().toJson(message);
- String expected = "{\"event\":\"event\",\"properties\":{\"key\":\"value\"}}";
-
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
- }
-
- @Test
- public void otherProperties() {
- CastleMessage message = CastleMessage.builder("event")
- .put("key", "value")
- .build();
-
- String payloadJson = model.getGson().toJson(message);
-
- Assertions.assertThat(payloadJson).isEqualTo("{\"event\":\"event\",\"key\":\"value\"}");
-
- HashMap other = new HashMap();
- other.put("key", "value");
-
- message = CastleMessage.builder("event")
- .other(other)
- .build();
-
- payloadJson = model.getGson().toJson(message);
-
- Assertions.assertThat(payloadJson).isEqualTo("{\"event\":\"event\",\"key\":\"value\"}");
- }
-
- @Test(expected = NullPointerException.class)
- public void propertiesNull() {
- CastleMessage message = CastleMessage.builder("event")
- .properties(null)
- .build();
- }
-
- @Test(expected = NullPointerException.class)
- public void userTraitsNull() {
- CastleMessage message = CastleMessage.builder("event")
- .userTraits(null)
- .build();
- }
-}
diff --git a/src/test/java/io/castle/client/model/CastleUserDeviceContextTest.java b/src/test/java/io/castle/client/model/CastleUserDeviceContextTest.java
deleted file mode 100644
index b46563bf..00000000
--- a/src/test/java/io/castle/client/model/CastleUserDeviceContextTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.JsonParser;
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.utils.DeviceUtils;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CastleUserDeviceContextTest {
- private CastleGsonModel model = new CastleGsonModel();
-
- @Test
- public void jsonSerialized() {
- // Given
- CastleUserDeviceContext deviceContext = new CastleUserDeviceContext();
-
- // When
- String payloadJson = model.getGson().toJson(deviceContext);
-
- // Then
- Assertions.assertThat(payloadJson).isEqualTo("{}");
- }
-
- @Test
- public void fullBuilderJson() {
- // Given
- CastleUserDeviceContext deviceContext = new CastleUserDeviceContext();
- deviceContext.setIp(DeviceUtils.CONTEXT_IP);
- deviceContext.setType(DeviceUtils.CONTEXT_TYPE);
-
- DeviceUserAgent userAgent = new DeviceUserAgent();
- userAgent.setBrowser(DeviceUtils.USER_AGENT_BROWSER);
- userAgent.setDevice(DeviceUtils.USER_AGENT_DEVICE);
- userAgent.setFamily(DeviceUtils.USER_AGENT_FAMILY);
- userAgent.setMobile(DeviceUtils.USER_AGENT_MOBILE);
- userAgent.setOs(DeviceUtils.USER_AGENT_OS);
- userAgent.setPlatform(DeviceUtils.USER_AGENT_PLATFORM);
- userAgent.setRaw(DeviceUtils.USER_AGENT_RAW);
- userAgent.setVersion(DeviceUtils.USER_AGENT_VERSION);
- deviceContext.setUserAgent(userAgent);
-
- // When
- String payloadJson = model.getGson().toJson(deviceContext);
- String expected = "{\"ip\":\"1.1.1.1\",\"user_agent\":{\"raw\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51\",\"browser\":\"Opera\",\"version\":\"54.0.2952\",\"os\":\"Mac OS X 10.13.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Opera\"},\"type\":\"desktop\"}";
-
- // Then
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
-
- Assert.assertEquals(deviceContext.getIp(), DeviceUtils.CONTEXT_IP);
- Assert.assertEquals(deviceContext.getType(), DeviceUtils.CONTEXT_TYPE);
- Assert.assertEquals(deviceContext.getUserAgent(), userAgent);
- }
-}
diff --git a/src/test/java/io/castle/client/model/CastleUserDeviceTest.java b/src/test/java/io/castle/client/model/CastleUserDeviceTest.java
deleted file mode 100644
index 8011231c..00000000
--- a/src/test/java/io/castle/client/model/CastleUserDeviceTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.JsonParser;
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.utils.DeviceUtils;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class CastleUserDeviceTest {
-
- private final CastleGsonModel model = new CastleGsonModel();
-
- @Test
- public void jsonSerialized() {
- // Given
- CastleUserDevice device = new CastleUserDevice();
-
- // When
- String payloadJson = model.getGson().toJson(device);
- String expected = "{\"risk\":0.0,\"is_current_device\":false}";
-
- // Then
- Assert.assertEquals(JsonParser.parseString(payloadJson), JsonParser.parseString(expected));
- }
-
- @Test
- public void fullBuilderJson() {
- // Given
- CastleUserDevice device = new CastleUserDevice();
- device.setToken(DeviceUtils.DEVICE_TOKEN);
- device.setCreatedAt(DeviceUtils.DEVICE_CREATED_AT);
- device.setLastSeenAt(DeviceUtils.DEVICE_LAST_SEEN_AT);
- device.setApprovedAt(DeviceUtils.DEVICE_APPROVED_AT);
- device.setEscalatedAt(DeviceUtils.DEVICE_ESCALATED_AT);
- device.setMitigatedAt(DeviceUtils.DEVICE_MITIGATED_AT);
- device.setCurrentDevice(true);
-
- CastleUserDeviceContext deviceContext = new CastleUserDeviceContext();
- deviceContext.setIp(DeviceUtils.CONTEXT_IP);
- deviceContext.setType(DeviceUtils.CONTEXT_TYPE);
-
- DeviceUserAgent userAgent = new DeviceUserAgent();
- userAgent.setBrowser(DeviceUtils.USER_AGENT_BROWSER);
- userAgent.setDevice(DeviceUtils.USER_AGENT_DEVICE);
- userAgent.setFamily(DeviceUtils.USER_AGENT_FAMILY);
- userAgent.setMobile(DeviceUtils.USER_AGENT_MOBILE);
- userAgent.setOs(DeviceUtils.USER_AGENT_OS);
- userAgent.setPlatform(DeviceUtils.USER_AGENT_PLATFORM);
- userAgent.setRaw(DeviceUtils.USER_AGENT_RAW);
- userAgent.setVersion(DeviceUtils.USER_AGENT_VERSION);
- deviceContext.setUserAgent(userAgent);
-
- device.setContext(deviceContext);
-
- // When
- String payloadJson = model.getGson().toJson(device);
- String expected = "{\"token\":\"abcdefg12345\",\"risk\":0.0,\"created_at\":\"2018-06-15T16:36:22.916Z\",\"last_seen_at\":\"2018-07-19T23:09:29.681Z\",\"context\":{\"ip\":\"1.1.1.1\",\"user_agent\":{\"raw\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51\",\"browser\":\"Opera\",\"version\":\"54.0.2952\",\"os\":\"Mac OS X 10.13.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Opera\"},\"type\":\"desktop\"},\"is_current_device\":true}";
-
- // Then
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
-
- Assert.assertEquals(device.getToken(), DeviceUtils.DEVICE_TOKEN);
- Assert.assertEquals(device.getCreatedAt(), DeviceUtils.DEVICE_CREATED_AT);
- Assert.assertEquals(device.getLastSeenAt(), DeviceUtils.DEVICE_LAST_SEEN_AT);
- Assert.assertEquals(device.getApprovedAt(), DeviceUtils.DEVICE_APPROVED_AT);
- Assert.assertEquals(device.getEscalatedAt(), DeviceUtils.DEVICE_ESCALATED_AT);
- Assert.assertEquals(device.getMitigatedAt(), DeviceUtils.DEVICE_MITIGATED_AT);
- Assert.assertTrue(device.isCurrentDevice());
- Assert.assertEquals(device.getContext(), deviceContext);
- }
-}
diff --git a/src/test/java/io/castle/client/model/CastleUserDevicesTest.java b/src/test/java/io/castle/client/model/CastleUserDevicesTest.java
deleted file mode 100644
index a78050b0..00000000
--- a/src/test/java/io/castle/client/model/CastleUserDevicesTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.JsonParser;
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.utils.DeviceUtils;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CastleUserDevicesTest {
- private CastleGsonModel model = new CastleGsonModel();
-
- @Test
- public void jsonSerialized() {
- // Given
- CastleUserDevices devices = new CastleUserDevices();
-
- // When
- String payloadJson = model.getGson().toJson(devices);
-
- // Then
- Assertions.assertThat(payloadJson).isEqualTo("{\"total_count\":0}");
- }
-
- @Test
- public void fullBuilderJson() {
- // Given
- CastleUserDevices devices = new CastleUserDevices();
-
- List devicesList = new ArrayList<>();
-
- CastleUserDevice device = new CastleUserDevice();
- device.setToken(DeviceUtils.DEVICE_TOKEN);
- device.setCreatedAt(DeviceUtils.DEVICE_CREATED_AT);
- device.setLastSeenAt(DeviceUtils.DEVICE_LAST_SEEN_AT);
- device.setApprovedAt(DeviceUtils.DEVICE_APPROVED_AT);
- device.setEscalatedAt(DeviceUtils.DEVICE_ESCALATED_AT);
- device.setMitigatedAt(DeviceUtils.DEVICE_MITIGATED_AT);
- device.setCurrentDevice(true);
-
- CastleUserDeviceContext deviceContext = new CastleUserDeviceContext();
- deviceContext.setIp(DeviceUtils.CONTEXT_IP);
- deviceContext.setType(DeviceUtils.CONTEXT_TYPE);
-
- DeviceUserAgent userAgent = new DeviceUserAgent();
- userAgent.setBrowser(DeviceUtils.USER_AGENT_BROWSER);
- userAgent.setDevice(DeviceUtils.USER_AGENT_DEVICE);
- userAgent.setFamily(DeviceUtils.USER_AGENT_FAMILY);
- userAgent.setMobile(DeviceUtils.USER_AGENT_MOBILE);
- userAgent.setOs(DeviceUtils.USER_AGENT_OS);
- userAgent.setPlatform(DeviceUtils.USER_AGENT_PLATFORM);
- userAgent.setRaw(DeviceUtils.USER_AGENT_RAW);
- userAgent.setVersion(DeviceUtils.USER_AGENT_VERSION);
- deviceContext.setUserAgent(userAgent);
-
- device.setContext(deviceContext);
-
- devicesList.add(device);
-
- devices.setDevices(devicesList);
-
- // When
- String payloadJson = model.getGson().toJson(devices);
- String expected = "{\"total_count\":1,\"data\":[{\"token\":\"abcdefg12345\",\"risk\":0.0,\"created_at\":\"2018-06-15T16:36:22.916Z\",\"last_seen_at\":\"2018-07-19T23:09:29.681Z\",\"context\":{\"ip\":\"1.1.1.1\",\"user_agent\":{\"raw\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51\",\"browser\":\"Opera\",\"version\":\"54.0.2952\",\"os\":\"Mac OS X 10.13.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Opera\"},\"type\":\"desktop\"},\"is_current_device\":true}]}";
-
- // Then
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
-
- Assert.assertEquals(devices.getTotalCount(), devicesList.size());
- Assert.assertEquals(devices.getDevices(), devicesList);
-
- devices.setDevices(null);
- Assert.assertEquals(0, devices.getTotalCount());
- }
-}
diff --git a/src/test/java/io/castle/client/model/DeviceUserAgentTest.java b/src/test/java/io/castle/client/model/DeviceUserAgentTest.java
deleted file mode 100644
index 022f48ca..00000000
--- a/src/test/java/io/castle/client/model/DeviceUserAgentTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package io.castle.client.model;
-
-import com.google.gson.JsonParser;
-import io.castle.client.internal.json.CastleGsonModel;
-import io.castle.client.utils.DeviceUtils;
-import org.assertj.core.api.Assertions;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class DeviceUserAgentTest {
- private CastleGsonModel model = new CastleGsonModel();
-
- @Test
- public void jsonSerialized() {
- // Given
- DeviceUserAgent userAgent = new DeviceUserAgent();
-
- // When
- String payloadJson = model.getGson().toJson(userAgent);
-
- // Then
- Assertions.assertThat(payloadJson).isEqualTo("{\"mobile\":false}");
- }
-
- @Test
- public void fullBuilderJson() {
- // Given
- DeviceUserAgent userAgent = new DeviceUserAgent();
- userAgent.setBrowser(DeviceUtils.USER_AGENT_BROWSER);
- userAgent.setDevice(DeviceUtils.USER_AGENT_DEVICE);
- userAgent.setFamily(DeviceUtils.USER_AGENT_FAMILY);
- userAgent.setMobile(DeviceUtils.USER_AGENT_MOBILE);
- userAgent.setOs(DeviceUtils.USER_AGENT_OS);
- userAgent.setPlatform(DeviceUtils.USER_AGENT_PLATFORM);
- userAgent.setRaw(DeviceUtils.USER_AGENT_PLATFORM);
- userAgent.setVersion(DeviceUtils.USER_AGENT_PLATFORM);
-
- // When
- String payloadJson = model.getGson().toJson(userAgent);
- String expected = "{\"raw\":\"Mac OS X\",\"browser\":\"Opera\",\"version\":\"Mac OS X\",\"os\":\"Mac OS X 10.13.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Opera\"}";
- // Then
- Assertions.assertThat(JsonParser.parseString(payloadJson)).isEqualTo(JsonParser.parseString(expected));
-
- Assert.assertEquals(userAgent.getBrowser(), DeviceUtils.USER_AGENT_BROWSER);
- Assert.assertEquals(userAgent.getDevice(), DeviceUtils.USER_AGENT_DEVICE);
- Assert.assertEquals(userAgent.getFamily(), DeviceUtils.USER_AGENT_FAMILY);
- Assert.assertEquals(userAgent.isMobile(), DeviceUtils.USER_AGENT_MOBILE);
- Assert.assertEquals(userAgent.getOs(), DeviceUtils.USER_AGENT_OS);
- Assert.assertEquals(userAgent.getPlatform(), DeviceUtils.USER_AGENT_PLATFORM);
- Assert.assertEquals(userAgent.getRaw(), DeviceUtils.USER_AGENT_PLATFORM);
- Assert.assertEquals(userAgent.getVersion(), DeviceUtils.USER_AGENT_PLATFORM);
- }
-}
diff --git a/src/test/java/io/castle/client/utils/DeviceUtils.java b/src/test/java/io/castle/client/utils/DeviceUtils.java
deleted file mode 100644
index b1692e68..00000000
--- a/src/test/java/io/castle/client/utils/DeviceUtils.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package io.castle.client.utils;
-
-import io.castle.client.model.CastleUserDevice;
-import io.castle.client.model.CastleUserDeviceContext;
-import io.castle.client.model.CastleUserDevices;
-import io.castle.client.model.DeviceUserAgent;
-
-import java.util.Collections;
-
-public class DeviceUtils {
-
- public static final String DEVICE_TOKEN = "abcdefg12345";
- public static final double DEVICE_RISK = 0.000154;
- public static final String DEVICE_CREATED_AT = "2018-06-15T16:36:22.916Z";
- public static final String DEVICE_LAST_SEEN_AT = "2018-07-19T23:09:29.681Z";
- public static final String DEVICE_APPROVED_AT = null;
- public static final String DEVICE_ESCALATED_AT = null;
- public static final String DEVICE_MITIGATED_AT = null;
-
- public static final String CONTEXT_TYPE = "desktop";
- public static final String CONTEXT_IP = "1.1.1.1";
-
- public static final String USER_AGENT_FAMILY = "Opera";
- public static final String USER_AGENT_DEVICE = "Unknown";
- public static final String USER_AGENT_PLATFORM = "Mac OS X";
- public static final String USER_AGENT_OS = "Mac OS X 10.13.6";
- public static final String USER_AGENT_VERSION = "54.0.2952";
- public static final String USER_AGENT_BROWSER = "Opera";
- public static final String USER_AGENT_RAW = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51";
- public static final boolean USER_AGENT_MOBILE = false;
-
- public static CastleUserDevice createExpectedDevice() {
- CastleUserDevice device = new CastleUserDevice();
- device.setToken(DEVICE_TOKEN);
- device.setRisk(DEVICE_RISK);
- device.setCreatedAt(DEVICE_CREATED_AT);
- device.setLastSeenAt(DEVICE_LAST_SEEN_AT);
- device.setApprovedAt(null);
- device.setEscalatedAt(null);
- device.setMitigatedAt(null);
-
- CastleUserDeviceContext deviceContext = new CastleUserDeviceContext();
- deviceContext.setIp(CONTEXT_IP);
- deviceContext.setType(CONTEXT_TYPE);
-
- DeviceUserAgent userAgent = new DeviceUserAgent();
- userAgent.setFamily(USER_AGENT_FAMILY);
- userAgent.setDevice(USER_AGENT_DEVICE);
- userAgent.setPlatform(USER_AGENT_PLATFORM);
- userAgent.setOs(USER_AGENT_OS);
- userAgent.setVersion(USER_AGENT_VERSION);
- userAgent.setBrowser(USER_AGENT_BROWSER);
- userAgent.setRaw(USER_AGENT_RAW);
- userAgent.setMobile(USER_AGENT_MOBILE);
-
- deviceContext.setUserAgent(userAgent);
-
- device.setContext(deviceContext);
-
- return device;
- }
-
- public static CastleUserDevices createExpectedDevices() {
- CastleUserDevices devices = new CastleUserDevices();
- devices.setDevices(Collections.singletonList(createExpectedDevice()));
- return devices;
- }
-
- public static String getDefaultDeviceJSON() {
- return "{\"token\":\"abcdefg12345\",\"risk\":0.000154,\"created_at\":\"2018-06-15T16:36:22.916Z\",\"last_seen_at\":\"2018-07-19T23:09:29.681Z\",\"approved_at\":null,\"escalated_at\":null,\"mitigated_at\":null,\"context\":{\"ip\":\"1.1.1.1\",\"location\":{\"country_code\":\"US\",\"country\":\"United States\",\"region\":\"New Jersey\",\"region_code\":\"NJ\",\"city\":\"Lyndhurst\",\"lat\":40.7923,\"lon\":-74.1001},\"user_agent\":{\"raw\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51\",\"browser\":\"Opera\",\"version\":\"54.0.2952\",\"os\":\"Mac OS X 10.13.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Opera\"},\"type\":\"desktop\"}}";
- }
-}
diff --git a/src/test/resources/castle_sdk.properties b/src/test/resources/castle_sdk.properties
index c22bb141..2b8d1b56 100644
--- a/src/test/resources/castle_sdk.properties
+++ b/src/test/resources/castle_sdk.properties
@@ -4,5 +4,4 @@ allow_list=TestAllow,User-Agent,Accept-Language,Accept-Encoding,Accept-Charset,A
deny_list=TestDeny,Cookie
timeout=100
backend_provider=OKHTTP
-failover_strategy=CHALLENGE
base_url=https://testing.api.dev.castle/v1/