265 lines
9.6 KiB
Java
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());
|
|
}
|
|
}
|
|
}
|