Skip to content

Commit

Permalink
Add generic request api (#97)
Browse files Browse the repository at this point in the history
* Add generic request implementation

* Update path param name

* Add test for generic api requests

* Move api paths

* Add payload keys

* Rename url param to path

* Use constant for http methods

* Return CastleResponse instead of JsonElement for generic api methods

* Add support for filter, log and risk api (#98)

* Add filter, log and risk api

* Add examples

* Add context and user id keys

* Update method doc param to payload

* Add exceptions to examples in readme
  • Loading branch information
sebastiansimson authored Jun 3, 2021
1 parent e47cabf commit cf809e1
Show file tree
Hide file tree
Showing 8 changed files with 616 additions and 9 deletions.
114 changes: 112 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,116 @@ Note that the `req` instance should be bound to the underlying request in order
It means that a safe place to create the `CastleApi` instance is the request handling thread. After creation the
`CastleApi` instance can be passed to any thread independently of the original thread life cycle.

## Log

```java
Castle castle = Castle.initialize();

CastleContext context = castle.contextBuilder()
.fromHttpServletRequest(request)
.build();

try {
CastleResponse response = castle.client().log(ImmutableMap.builder()
.put(Castle.KEY_EVENT, "$login")
.put(Castle.KEY_CONTEXT, ImmutableMap.builder()
.put(Castle.KEY_IP, context.getIp())
.put(Castle.KEY_HEADERS, context.getHeaders())
.build()
)
.put(Castle.KEY_USER, ImmutableMap.builder()
.put(Castle.KEY_USER_ID, user.getId())
.put(Castle.KEY_EMAIL, user.getEmail())
.put("username", user.getUsername())
.build()
)
.build()
);
} catch (CastleApiTimeoutException apiTimeoutException) {
// Timeout
} catch (CastleApiInternalServerErrorException apiInternalServerErrorException) {
// Internal Server error (500)
} catch (CastleServerErrorException castleServerErrorException) {
// Server error
} catch (CastleRuntimeException runtimeException) {
// Generic exception
}
```

## Filter

```java
Castle castle = Castle.initialize();

CastleContext context = castle.contextBuilder()
.fromHttpServletRequest(request)
.build();

try {
CastleResponse response = castle.client().filter(ImmutableMap.builder()
.put(Castle.KEY_EVENT, "$login")
.put(Castle.KEY_CONTEXT, ImmutableMap.builder()
.put(Castle.KEY_IP, context.getIp())
.put(Castle.KEY_HEADERS, context.getHeaders())
build()
)
.put(Castle.KEY_USER, ImmutableMap.builder()
.put(Castle.KEY_USER_ID, user.getId())
.put(Castle.KEY_EMAIL, user.getEmail())
.put("username", user.getUsername())
.build()
)
.put(Castle.KEY_REQUEST_TOKEN, "0af87174-37b4-4adc-a1a6-eb")
.build()
);
} catch (CastleApiTimeoutException apiTimeoutException) {
// Timeout
} catch (CastleApiInternalServerErrorException apiInternalServerErrorException) {
// Internal Server error (500)
} catch (CastleServerErrorException castleServerErrorException) {
// Server error
} catch (CastleRuntimeException runtimeException) {
// Generic exception
}
```

## Filter

```java
Castle castle = Castle.initialize();

CastleContext context = castle.contextBuilder()
.fromHttpServletRequest(request)
.build();

try {
CastleResponse response = castle.client().risk(ImmutableMap.builder()
.put(Castle.KEY_EVENT, "$login")
.put(Castle.KEY_CONTEXT, ImmutableMap.builder()
.put(Castle.KEY_IP, context.getIp())
.put(Castle.KEY_HEADERS, context.getHeaders())
build()
)
.put(Castle.KEY_USER, ImmutableMap.builder()
.put(Castle.KEY_USER_ID, user.getId())
.put(Castle.KEY_EMAIL, user.getEmail())
.put("username", user.getUsername())
.build()
)
.put(Castle.KEY_REQUEST_TOKEN, "0af87174-37b4-4adc-a1a6-eb")
.build()
);
} catch (CastleApiTimeoutException apiTimeoutException) {
// Timeout
} catch (CastleApiInternalServerErrorException apiInternalServerErrorException) {
// Internal Server error (500)
} catch (CastleServerErrorException castleServerErrorException) {
// Server error
} catch (CastleRuntimeException runtimeException) {
// Generic exception
}
```

## The Context Object

The context object contains information about the request sent by the end-user,
Expand All @@ -140,7 +250,7 @@ such as IP and UserAgent
// Quick way of building context through the incoming HttpServletRequest
CastleContext context = castle.contextBuilder()
.fromHttpServletRequest(request)
.build()
.build();

// or build context manually
CastleContext context = castle.contextBuilder()
Expand Down Expand Up @@ -202,7 +312,7 @@ String jsonContext = castle.contextBuilder()
// Convert json back to a CastleContext
CastleContext context = castle.contextBuilder()
.fromJson(jsonContext)
.build()
.build();

// Send the tracking request
castle.client().track(CastleMessage.builder("$login.failed")
Expand Down
94 changes: 94 additions & 0 deletions src/main/java/io/castle/client/Castle.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.castle.client;

import com.google.common.collect.ImmutableMap;
import com.google.common.hash.HashFunction;
import io.castle.client.api.CastleApi;
import io.castle.client.internal.CastleApiImpl;
Expand All @@ -8,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.model.CastleResponse;
import io.castle.client.model.CastleSdkConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -24,6 +26,36 @@
* Once set the {@code this#instance()} method will return 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 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 KEY_EVENT = "event";
public static final String KEY_USER = "user";
public static final String KEY_STATUS = "status";
public static final String KEY_FINGERPRINT = "fingerprint";
public static final String KEY_REGISTERED_AT = "registered_at";
public static final String KEY_CREATED_AT = "created_at";
public static final String KEY_PROPERTIES = "properties";
public static final String KEY_REQUEST_TOKEN = "request_token";
public static final String KEY_CONTEXT = "context";

// Context
public static final String KEY_IP = "ip";
public static final String KEY_HEADERS = "headers";

// User
public static final String KEY_USER_ID = "user_id";
public static final String KEY_NAME = "name";
public static final String KEY_EMAIL = "email";
public static final String KEY_TRAITS = "traits";

public static final Logger logger = LoggerFactory.getLogger(Castle.class);

private final CastleSdkInternalConfiguration internalConfiguration;
Expand Down Expand Up @@ -221,4 +253,66 @@ public String secureUserID(String userId) {
HashFunction hashFunction = internalConfiguration.getSecureHashFunction();
return hashFunction.hashString(userId,com.google.common.base.Charsets.UTF_8).toString();
}

/**
* Make a GET request to a Castle API endpoint such as /v1/{userId}/devices
*
* @param path api path
* @return a decoded json response
*/
public CastleResponse get(String path) {
return client().get(path);
}

/**
* Make a POST request to a Castle API endpoint such as /v1/track
*
* @param path api path
* @param payload request payload
* @return a decoded json response
*/
public CastleResponse post(String path, ImmutableMap<String, Object> payload) {
return client().post(path, payload);
}
/**
* Make a PUT request to a Castle API endpoint such as /v1/devices/{deviceToken}/report
*
* @param path api path
* @return a decoded json response
*/
public CastleResponse put(String path) {
return client().put(path);
}

/**
* Make a PUT request to a Castle API endpoint such as /v1/devices/{deviceToken}/report
*
* @param path api path
* @param payload request payload
* @return a decoded json response
*/
public CastleResponse put(String path, ImmutableMap<String, Object> payload) {
return client().put(path, payload);
}

/**
* Make a DELETE request to a Castle API endpoint such as /v1/impersonate
*
* @param path api path
* @return a decoded json response
*/
public CastleResponse delete(String path) {
return client().delete(path);
}

/**
* Make a DELETE request to a Castle API endpoint such as /v1/impersonate
*
* @param path api path
* @param payload request payload
* @return a decoded json response
*/
public CastleResponse delete(String path, ImmutableMap<String, Object> payload) {
return client().delete(path, payload);
}
}
37 changes: 37 additions & 0 deletions src/main/java/io/castle/client/api/CastleApi.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.castle.client.api;

import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import io.castle.client.model.*;

Expand Down Expand Up @@ -285,4 +286,40 @@ public interface CastleApi {
* @return
*/
CastleSuccess impersonateEnd(String userId, String impersonator);

CastleResponse get(String path);

CastleResponse post(String path, ImmutableMap<String, Object> payload);

CastleResponse put(String path);

CastleResponse put(String path, ImmutableMap<String, Object> payload);

CastleResponse delete(String path);

CastleResponse delete(String path, ImmutableMap<String, Object> payload);

/**
* Makes a sync POST request to the risk endpoint.
*
* @param payload Event parameters
* @return
*/
CastleResponse risk(ImmutableMap<String, Object> payload);

/**
* Makes a sync POST request to the filter endpoint.
*
* @param payload Event parameters
* @return
*/
CastleResponse filter(ImmutableMap<String, Object> payload);

/**
* Makes a sync POST request to the log endpoint.
*
* @param payload Event parameters
* @return
*/
CastleResponse log(ImmutableMap<String, Object> payload);
}
62 changes: 62 additions & 0 deletions src/main/java/io/castle/client/internal/CastleApiImpl.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package io.castle.client.internal;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.castle.client.Castle;
import io.castle.client.api.CastleApi;
import io.castle.client.internal.backend.RestApi;
import io.castle.client.internal.config.CastleSdkInternalConfiguration;
Expand Down Expand Up @@ -276,11 +278,71 @@ public CastleSuccess impersonateEnd(String userId, String impersonator) {
return restApi.sendImpersonateEndRequestSync(userId, impersonator, contextJson);
}

@Override
public CastleResponse get(String path) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.get(path);
}

@Override
public CastleResponse post(String path, ImmutableMap<String, Object> payload) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.post(path, payload);
}

@Override
public CastleResponse put(String path) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.put(path);
}

@Override
public CastleResponse put(String path, ImmutableMap<String, Object> payload) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.put(path, payload);
}

@Override
public CastleResponse delete(String path) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.delete(path);
}

@Override
public CastleResponse delete(String path, ImmutableMap<String, Object> payload) {
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.delete(path, payload);
}

public CastleResponse risk(ImmutableMap<String, Object> payload) {
Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.post(Castle.URL_RISK, payload);
}

@Override
public CastleResponse filter(ImmutableMap<String, Object> payload) {
Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.post(Castle.URL_FILTER, payload);
}

@Override
public CastleResponse log(ImmutableMap<String, Object> payload) {
Preconditions.checkNotNull(payload);
RestApi restApi = configuration.getRestApiFactory().buildBackend();
return restApi.post(Castle.URL_LOG, payload);
}

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);
Expand Down
Loading

0 comments on commit cf809e1

Please sign in to comment.