Skip to content

Commit

Permalink
feat: adds Request and Response Hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
jchen293 committed Jul 5, 2023
1 parent 83c0312 commit dadb478
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## Next release

- Adds new `RequestHook` and `ResponseHook` classes. (un)subscribe to them with the new `subscribeToRequestHook`, `subscribeToResponseHook`, `unsubscribeFromRequestHook`, or `unsubscribeFromResponseHook` methods of an `EasyPostClient`

## v6.7.0 (2023-06-06)

- Retrieving carrier metadata is now generally available via `client.carrierMetadata.retrieve`
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ public class CreateShipment {
}
```

## HTTP Hooks

Users can subscribe to HTTP requests and responses via the `RequestHook` and `ResponseHook` objects. To do so, pass a function to the `subscribeToRequest_hook` or `subscribeToResponse_hook` methods of an `EasyPostClient` object:

```java
public static Object customFunction(HashMap<String, Object> datas) {
// Pass your code here, the information about the request/response is available within the datas parameter.
for (Map.Entry<String, Object> entry : datas.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
System.out.println("Key: " + key + ", Value: " + value);
}

return true;
}

EasyPostClient client = new EasyPostClient(System.getenv("EASYPOST_API_KEY"));

client.subscribeToRequestHook(customFunction); // subscribe to request hook by passing your custom function
client.unsubscribeToRequestHook(customFunction); // unsubscribe from request hook
```
## Documentation

API documentation can be found at: <https://easypost.com/docs/api>.
Expand Down
1 change: 1 addition & 0 deletions dependency-check-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
<vulnerabilityName>CVE-2022-3171</vulnerabilityName>
<vulnerabilityName>CVE-2022-3509</vulnerabilityName>
<vulnerabilityName>CVE-2022-3510</vulnerabilityName>
<vulnerabilityName>CVE-2023-2976</vulnerabilityName>
</suppress>
</suppressions>
36 changes: 36 additions & 0 deletions src/main/java/com/easypost/hooks/EventHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.easypost.hooks;

import java.util.function.Function;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class EventHook {
private List<Function<HashMap<String, Object>, Object>> eventHandlers = new ArrayList<>();

/**
* Add a function to the list of event handlers.
* @param handler The event handler function to be added.
*/
public void addEventHandler(Function<HashMap<String, Object>, Object> handler) {
eventHandlers.add(handler);
}

/**
* Remove a function to the list of event handlers.
* @param handler The event handler function to be removed.
*/
public void removeEventHandler(Function<HashMap<String, Object>, Object> handler) {
eventHandlers.remove(handler);
}

/**
* Execute all the functions from the event handlers.
* @param datas The datas from the hooks.
*/
public void executeEventHandler(HashMap<String, Object> datas) {
for (Function<HashMap<String, Object>, Object> eventHandler : eventHandlers) {
Object result = eventHandler.apply(datas);
}
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/easypost/hooks/RequestHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.easypost.hooks;

public class RequestHook extends EventHook {
}
4 changes: 4 additions & 0 deletions src/main/java/com/easypost/hooks/ResponseHook.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.easypost.hooks;

public class ResponseHook extends EventHook {
}
9 changes: 9 additions & 0 deletions src/main/java/com/easypost/hooks/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Custom hook classes for the EasyPost API.
*
* @author EasyPost developers
* @version 1.0
* @see <a href="https://www.easypost.com/docs">EasyPost API documentation</a>
* @since 1.0
*/
package com.easypost.hooks;
39 changes: 35 additions & 4 deletions src/main/java/com/easypost/http/Requestor.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLStreamHandler;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.UUID;

public abstract class Requestor {
public enum RequestMethod {
Expand Down Expand Up @@ -417,6 +419,7 @@ private static EasyPostResponse makeURLConnectionRequest(final RequestMethod met
* @throws PaymentError when the request requires payment.
* @throws NotFoundError when the request endpoint is not found.
* @throws MethodNotAllowedError when the request method is not allowed.
* @throws MissingParameterError when the request client doesn't have API key.
* @throws TimeoutError when the request times out.
* @throws InvalidRequestError when the request is invalid.
* @throws RateLimitError when the request exceeds the rate limit.
Expand All @@ -429,7 +432,7 @@ public static <T> T request(final RequestMethod method, final String endpoint, f
final Class<T> clazz, final EasyPostClient client)
throws GatewayTimeoutError, RateLimitError, InvalidRequestError, NotFoundError, TimeoutError, EncodingError,
UnauthorizedError, MethodNotAllowedError, InternalServerError, UnknownApiError, ServiceUnavailableError,
ForbiddenError, JsonError, HttpError, RedirectError, PaymentError {
ForbiddenError, JsonError, HttpError, RedirectError, PaymentError, MissingParameterError {
String apiVersion = client.getApiVersion();
return request(method, endpoint, params, clazz, client, apiVersion);
}
Expand All @@ -454,6 +457,7 @@ public static <T> T request(final RequestMethod method, final String endpoint, f
* @throws PaymentError when the request requires payment.
* @throws NotFoundError when the request endpoint is not found.
* @throws MethodNotAllowedError when the request method is not allowed.
* @throws MissingParameterError when the request client doesn't have API key.
* @throws TimeoutError when the request times out.
* @throws InvalidRequestError when the request is invalid.
* @throws RateLimitError when the request exceeds the rate limit.
Expand All @@ -466,7 +470,8 @@ public static <T> T request(final RequestMethod method, final String endpoint, f
final Class<T> clazz, final EasyPostClient client, final String apiVersion)
throws EncodingError, JsonError, RedirectError, UnauthorizedError, ForbiddenError, PaymentError,
NotFoundError, MethodNotAllowedError, TimeoutError, InvalidRequestError, RateLimitError,
InternalServerError, ServiceUnavailableError, GatewayTimeoutError, UnknownApiError, HttpError {
InternalServerError, ServiceUnavailableError, GatewayTimeoutError, UnknownApiError, HttpError,
MissingParameterError {
String originalDNSCacheTTL = null;
boolean allowedToSetTTL = true;
String url = client.getApiBase() + "/" + apiVersion + "/" + endpoint;
Expand Down Expand Up @@ -511,6 +516,7 @@ public static <T> T request(final RequestMethod method, final String endpoint, f
* @throws PaymentError when the request requires payment.
* @throws NotFoundError when the request endpoint is not found.
* @throws MethodNotAllowedError when the request method is not allowed.
* @throws MissingParameterError when the request client doesn't have API key.
* @throws TimeoutError when the request times out.
* @throws InvalidRequestError when the request is invalid.
* @throws RateLimitError when the request exceeds the rate limit.
Expand All @@ -524,7 +530,8 @@ private static <T> T httpRequest(final RequestMethod method, final String url, f
final Class<T> clazz, final EasyPostClient client)
throws EncodingError, JsonError, RedirectError, UnauthorizedError, ForbiddenError, PaymentError,
NotFoundError, MethodNotAllowedError, TimeoutError, InvalidRequestError, RateLimitError,
InternalServerError, ServiceUnavailableError, GatewayTimeoutError, UnknownApiError, HttpError {
InternalServerError, ServiceUnavailableError, GatewayTimeoutError, UnknownApiError, HttpError,
MissingParameterError {
String query = null;
JsonObject body = null;
if (params != null) {
Expand Down Expand Up @@ -553,7 +560,20 @@ private static <T> T httpRequest(final RequestMethod method, final String url, f
break;
}
}

Instant requestTimestamp = Instant.now();
UUID requestUuid = UUID.randomUUID();
Map<String, String> headers = new HashMap<String, String>();
headers = generateHeaders(client.getApiKey());

HashMap<String, Object> requestBodyForHook = new HashMap<String, Object>();
requestBodyForHook.put("headers", headers);
requestBodyForHook.put("method", method.toString());
requestBodyForHook.put("path", url);
requestBodyForHook.put("request_body", body);
requestBodyForHook.put("request_timestamp", requestTimestamp);
requestBodyForHook.put("request_uuid", requestUuid);
client.getRequestHooks().executeEventHandler(requestBodyForHook);

EasyPostResponse response;
try {
// HTTPSURLConnection verifies SSL cert by default
Expand All @@ -573,6 +593,17 @@ private static <T> T httpRequest(final RequestMethod method, final String url, f
handleAPIError(rBody, rCode);
}

HashMap<String, Object> responseBodyForHook = new HashMap<String, Object>();
responseBodyForHook.put("http_status", rCode);
responseBodyForHook.put("headers", headers);
responseBodyForHook.put("method", method.toString());
responseBodyForHook.put("path", url);
responseBodyForHook.put("response_body", rBody);
responseBodyForHook.put("response_timestamp", Instant.now());
responseBodyForHook.put("request_timestamp", requestTimestamp);
responseBodyForHook.put("request_uuid", requestUuid);
client.getResponseHooks().executeEventHandler(responseBodyForHook);

return Constants.Http.GSON.fromJson(rBody, clazz);
}

Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/easypost/service/AddressService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.easypost.service;

import com.easypost.exception.APIException;
import com.easypost.exception.EasyPostException;
import com.easypost.exception.General.EndOfPaginationError;
import com.easypost.http.Requestor;
Expand Down Expand Up @@ -71,7 +70,7 @@ public Address retrieve(final String id) throws EasyPostException {
* @return AddressCollection object.
* @throws APIException when the request fails.
*/
public AddressCollection all(final Map<String, Object> params) throws APIException {
public AddressCollection all(final Map<String, Object> params) throws EasyPostException {
String endpoint = "addresses";

return Requestor.request(RequestMethod.GET, endpoint, params, AddressCollection.class, client);
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/com/easypost/service/EasyPostClient.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package com.easypost.service;

import lombok.Getter;

import com.easypost.Constants;
import com.easypost.exception.General.MissingParameterError;
import com.easypost.hooks.ResponseHook;
import com.easypost.hooks.RequestHook;

import java.util.HashMap;
import java.util.function.Function;

public class EasyPostClient {
private final int connectTimeoutMilliseconds;
Expand Down Expand Up @@ -37,6 +44,10 @@ public class EasyPostClient {
public final TrackerService tracker;
public final UserService user;
public final WebhookService webhook;
@Getter
private RequestHook requestHooks = new RequestHook();
@Getter
private ResponseHook responseHooks = new ResponseHook();

/**
* EasyPostClient constructor.
Expand Down Expand Up @@ -145,6 +156,38 @@ public EasyPostClient(String apiKey, int connectTimeoutMilliseconds, int readTim
this.webhook = new WebhookService(this);
}

/**
* Subscribes to a request hook from the given function.
* @param function
*/
public void subscribeToRequestHook(Function<HashMap<String, Object>, Object> function) {
this.requestHooks.addEventHandler(function);
}

/**
* Unsubscribes to a request hook from the given function.
* @param function
*/
public void unsubscribeFromRequestHook(Function<HashMap<String, Object>, Object> function) {
this.requestHooks.removeEventHandler(function);
}

/**
* Subscribes to a response hook from the given function.
* @param function
*/
public void subscribeToResponseHook(Function<HashMap<String, Object>, Object> function) {
this.responseHooks.addEventHandler(function);
}

/**
* Unubscribes to a response hook from the given function.
* @param function
*/
public void unsubscribeFromResponseHook(Function<HashMap<String, Object>, Object> function) {
this.responseHooks.removeEventHandler(function);
}

/**
* Get connection timeout milliseconds for this EasyPostClient object.
*
Expand Down
97 changes: 97 additions & 0 deletions src/test/cassettes/hook/create.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit dadb478

Please sign in to comment.