Files
evercatch-java/src/main/java/dev/evercatch/EvercatchClient.java
Puranjay Savar Mattas e66227afc4
All checks were successful
PR Management Bot / pr-bot (pull_request) Successful in 7s
PR Title Checker / Validate PR Title Format (pull_request) Successful in 2s
EC-21: FEAT: Added new Java package and initialized the repo
Refers Evercatch/evercatch-board#21
2026-02-19 21:24:58 +00:00

265 lines
9.6 KiB
Java

package dev.evercatch;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import dev.evercatch.http.HttpClient;
import dev.evercatch.model.*;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.Response;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Evercatch API client for Java.
*
* <p>Example usage:</p>
* <pre>{@code
* EvercatchClient client = new EvercatchClient("ec_live_abc123");
*
* // Create a destination
* Destination dest = client.createDestination(
* CreateDestinationRequest.builder()
* .name("Production")
* .url("https://myapp.com/webhooks")
* .providers(List.of("stripe", "sendgrid"))
* .build()
* );
*
* // List recent events
* List<Event> events = client.listEvents(
* ListEventsRequest.builder()
* .provider("stripe")
* .limit(50)
* .build()
* );
* }</pre>
*/
public class EvercatchClient {
private static final String DEFAULT_BASE_URL = "https://api.evercatch.dev/v1";
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
private final String baseUrl;
private final HttpClient httpClient;
private final Gson gson;
/**
* Creates a client using the default API base URL.
*
* @param apiKey your Evercatch API key (starts with {@code ec_live_} or {@code ec_test_})
*/
public EvercatchClient(String apiKey) {
this(apiKey, DEFAULT_BASE_URL);
}
/**
* Creates a client with a custom base URL (useful for self-hosted instances or testing).
*
* @param apiKey your Evercatch API key
* @param baseUrl custom API base URL
*/
public EvercatchClient(String apiKey, String baseUrl) {
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new IllegalArgumentException("API key cannot be null or empty");
}
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
this.httpClient = new HttpClient(apiKey);
this.gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
.create();
}
// -------------------------------------------------------------------------
// Destinations
// -------------------------------------------------------------------------
/**
* Creates a new webhook destination.
*
* @param request destination configuration
* @return the created {@link Destination}
* @throws EvercatchException if the API request fails
*/
public Destination createDestination(CreateDestinationRequest request) throws EvercatchException {
String url = baseUrl + "/destinations";
RequestBody body = RequestBody.create(gson.toJson(request), JSON_MEDIA_TYPE);
try (Response response = httpClient.post(url, body)) {
handleError(response, "create destination");
return gson.fromJson(response.body().string(), Destination.class);
} catch (IOException e) {
throw new EvercatchException("Network error creating destination", e);
}
}
/**
* Returns all webhook destinations for the account.
*
* @return list of destinations
* @throws EvercatchException if the API request fails
*/
public List<Destination> listDestinations() throws EvercatchException {
String url = baseUrl + "/destinations";
try (Response response = httpClient.get(url)) {
handleError(response, "list destinations");
DestinationListResponse resp = gson.fromJson(
response.body().string(), DestinationListResponse.class);
return resp.getData();
} catch (IOException e) {
throw new EvercatchException("Network error listing destinations", e);
}
}
/**
* Retrieves a single destination by ID.
*
* @param id destination ID
* @return the matching {@link Destination}
* @throws EvercatchException if the API request fails
*/
public Destination getDestination(String id) throws EvercatchException {
String url = baseUrl + "/destinations/" + id;
try (Response response = httpClient.get(url)) {
handleError(response, "get destination");
return gson.fromJson(response.body().string(), Destination.class);
} catch (IOException e) {
throw new EvercatchException("Network error getting destination", e);
}
}
/**
* Deletes a destination.
*
* @param id destination ID
* @throws EvercatchException if the API request fails
*/
public void deleteDestination(String id) throws EvercatchException {
String url = baseUrl + "/destinations/" + id;
try (Response response = httpClient.delete(url)) {
handleError(response, "delete destination");
} catch (IOException e) {
throw new EvercatchException("Network error deleting destination", e);
}
}
// -------------------------------------------------------------------------
// Events
// -------------------------------------------------------------------------
/**
* Lists webhook events with optional filters.
*
* @param request filter / pagination options (use {@code ListEventsRequest.builder().build()}
* for defaults)
* @return list of events
* @throws EvercatchException if the API request fails
*/
public List<Event> listEvents(ListEventsRequest request) throws EvercatchException {
StringBuilder url = new StringBuilder(baseUrl).append("/events?");
Map<String, String> params = new HashMap<>();
if (request.getProvider() != null) params.put("provider", request.getProvider());
if (request.getEventType() != null) params.put("event_type", request.getEventType());
if (request.getStatus() != null) params.put("status", request.getStatus());
if (request.getLimit() != null) params.put("limit", request.getLimit().toString());
if (request.getCursor() != null) params.put("cursor", request.getCursor());
params.forEach((k, v) -> url.append(k).append("=").append(v).append("&"));
try (Response response = httpClient.get(url.toString())) {
handleError(response, "list events");
EventListResponse resp = gson.fromJson(
response.body().string(), EventListResponse.class);
return resp.getData();
} catch (IOException e) {
throw new EvercatchException("Network error listing events", e);
}
}
/**
* Retrieves a single event by ID.
*
* @param id event ID
* @return the matching {@link Event}
* @throws EvercatchException if the API request fails
*/
public Event getEvent(String id) throws EvercatchException {
String url = baseUrl + "/events/" + id;
try (Response response = httpClient.get(url)) {
handleError(response, "get event");
return gson.fromJson(response.body().string(), Event.class);
} catch (IOException e) {
throw new EvercatchException("Network error getting event", e);
}
}
/**
* Replays an event to the specified destinations.
*
* <p>Requires the Studio or Enterprise plan.</p>
*
* @param eventId ID of the event to replay
* @param destinationIds destination IDs to replay to
* @throws EvercatchException if the API request fails or the plan does not support replay
*/
public void replayEvent(String eventId, List<String> destinationIds) throws EvercatchException {
String url = baseUrl + "/events/" + eventId + "/replay";
Map<String, Object> payload = new HashMap<>();
payload.put("destination_ids", destinationIds);
RequestBody body = RequestBody.create(gson.toJson(payload), JSON_MEDIA_TYPE);
try (Response response = httpClient.post(url, body)) {
if (response.code() == 402) {
throw new EvercatchException("Event replay requires Studio or Enterprise plan", 402);
}
handleError(response, "replay event");
} catch (IOException e) {
throw new EvercatchException("Network error replaying event", e);
}
}
// -------------------------------------------------------------------------
// Account
// -------------------------------------------------------------------------
/**
* Returns current account usage statistics.
*
* @return {@link Usage} summary
* @throws EvercatchException if the API request fails
*/
public Usage getUsage() throws EvercatchException {
String url = baseUrl + "/account/usage";
try (Response response = httpClient.get(url)) {
handleError(response, "get usage");
return gson.fromJson(response.body().string(), Usage.class);
} catch (IOException e) {
throw new EvercatchException("Network error getting usage", e);
}
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
private void handleError(Response response, String operation) throws EvercatchException, IOException {
if (!response.isSuccessful()) {
String body = response.body() != null ? response.body().string() : "";
throw new EvercatchException(
String.format("Failed to %s (HTTP %d): %s", operation, response.code(), body),
response.code());
}
}
}