diff --git a/CHANGELOG.md b/CHANGELOG.md index 848622d10..6cad662d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -# [7.6.0] - 2023-06-29 +# [7.6.0] - 2023-06-30 - Added Proactive Connect API implementation - Added Meetings API implementation - Updated Subaccounts name & secret validation logic diff --git a/build.gradle b/build.gradle index 9d5f1cd56..bbe977f6b 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ repositories { dependencies { compileOnly 'jakarta.servlet:jakarta.servlet-api:4.0.4' - implementation 'commons-codec:commons-codec:1.15' + implementation 'commons-codec:commons-codec:1.16.0' implementation 'org.apache.httpcomponents:httpclient:4.5.14' implementation 'org.apache.httpcomponents:httpmime:4.5.14' implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' @@ -36,7 +36,6 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.11.0' - testImplementation 'org.hamcrest:hamcrest-all:1.3' testImplementation 'org.springframework:spring-test:5.3.28' testImplementation 'org.springframework:spring-web:5.3.28' testImplementation 'jakarta.servlet:jakarta.servlet-api:4.0.4' diff --git a/src/main/java/com/vonage/client/meetings/package-info.java b/src/main/java/com/vonage/client/meetings/package-info.java index e4c4b33be..78af1fb23 100644 --- a/src/main/java/com/vonage/client/meetings/package-info.java +++ b/src/main/java/com/vonage/client/meetings/package-info.java @@ -16,10 +16,9 @@ /** * This package contains classes and sub-packages to support usage of the - * Meetings API. - *
- * Please refer to - * the developer documentation for an overview of the concepts. + * Meetings API. Please refer to + * the developer documentation + * for an overview of the concepts. * * @since 7.6.0 */ diff --git a/src/main/java/com/vonage/client/proactiveconnect/Event.java b/src/main/java/com/vonage/client/proactiveconnect/Event.java index 74c15d646..461bdb52b 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/Event.java +++ b/src/main/java/com/vonage/client/proactiveconnect/Event.java @@ -121,7 +121,7 @@ public String getRecipientId() { } /** - * {@code src_ctx} field. + * The name of the segment or matcher. * * @return The source context or {@code null} if unknown. */ diff --git a/src/main/java/com/vonage/client/proactiveconnect/EventType.java b/src/main/java/com/vonage/client/proactiveconnect/EventType.java index 88c36ada2..5e82c9e0e 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/EventType.java +++ b/src/main/java/com/vonage/client/proactiveconnect/EventType.java @@ -22,15 +22,54 @@ * Represents values for {@link Event#getType()}. */ public enum EventType { + /** + * Action call succeeded + */ ACTION_CALL_SUCCEEDED, + + /** + * Action call failed + */ ACTION_CALL_FAILED, + + /** + * Action call info + */ ACTION_CALL_INFO, + + /** + * Recipient response + */ RECIPIENT_RESPONSE, + + /** + * Run item skipped + */ RUN_ITEM_SKIPPED, + + /** + * Run item failed + */ RUN_ITEM_FAILED, + + /** + * Run item submitted + */ RUN_ITEM_SUBMITTED, + + /** + * Run items total + */ RUN_ITEMS_TOTAL, + + /** + * Run items ready + */ RUN_ITEMS_READY, + + /** + * Run items excluded + */ RUN_ITEMS_EXCLUDED; @JsonCreator diff --git a/src/main/java/com/vonage/client/proactiveconnect/HalRequestWrapper.java b/src/main/java/com/vonage/client/proactiveconnect/HalRequestWrapper.java index 081a3aec3..6b89419d4 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/HalRequestWrapper.java +++ b/src/main/java/com/vonage/client/proactiveconnect/HalRequestWrapper.java @@ -19,11 +19,13 @@ class HalRequestWrapper { final Integer page, pageSize; + final SortOrder order; final String id; - HalRequestWrapper(Integer page, Integer pageSize, String id) { + HalRequestWrapper(Integer page, Integer pageSize, SortOrder order, String id) { this.page = page; this.pageSize = pageSize; + this.order = order; this.id = id; } @@ -34,6 +36,9 @@ RequestBuilder addParams(RequestBuilder builder) { if (pageSize != null) { builder.addParameter("page_size", pageSize.toString()); } + if (order != null) { + builder.addParameter("order", order.toString()); + } return builder; } } diff --git a/src/main/java/com/vonage/client/proactiveconnect/ListEventsEndpoint.java b/src/main/java/com/vonage/client/proactiveconnect/ListEventsEndpoint.java index 2bc2174ae..d10adc0f4 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/ListEventsEndpoint.java +++ b/src/main/java/com/vonage/client/proactiveconnect/ListEventsEndpoint.java @@ -22,7 +22,7 @@ import org.apache.http.client.methods.RequestBuilder; import java.io.IOException; -class ListEventsEndpoint extends AbstractMethod { +class ListEventsEndpoint extends AbstractMethod { private static final Class[] ALLOWED_AUTH_METHODS = {JWTAuthMethod.class}; private static final String PATH = "/v0.1/bulk/events"; @@ -36,10 +36,9 @@ protected Class[] getAcceptableAuthMethods() { } @Override - public RequestBuilder makeRequest(HalRequestWrapper request) { + public RequestBuilder makeRequest(ListEventsFilter request) { String uri = httpWrapper.getHttpConfig().getApiEuBaseUri() + PATH; - return request.addParams(RequestBuilder.get(uri)) - .setHeader("Accept", "application/json"); + return request.addParams(RequestBuilder.get(uri)).setHeader("Accept", "application/json"); } @Override diff --git a/src/main/java/com/vonage/client/proactiveconnect/ListEventsFilter.java b/src/main/java/com/vonage/client/proactiveconnect/ListEventsFilter.java new file mode 100644 index 000000000..9b363e393 --- /dev/null +++ b/src/main/java/com/vonage/client/proactiveconnect/ListEventsFilter.java @@ -0,0 +1,347 @@ +/* + * Copyright 2023 Vonage + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vonage.client.proactiveconnect; + +import org.apache.http.client.methods.RequestBuilder; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.UUID; + +/** + * Filter options for finding events using {@link ProactiveConnectClient#listEvents(ListEventsFilter)}. + */ +public class ListEventsFilter { + private final SortOrder order; + private final UUID runId, runItemId, invocationId, actionId, traceId; + private final String recipientId, sourceContext; + private final SourceType sourceType; + private final Instant startDate, endDate; + + ListEventsFilter(Builder builder) { + order = builder.order; + runId = builder.runId != null ? UUID.fromString(builder.runId) : null; + runItemId = builder.runItemId != null ? UUID.fromString(builder.runItemId) : null; + invocationId = builder.invocationId != null ? UUID.fromString(builder.invocationId) : null; + actionId = builder.actionId != null ? UUID.fromString(builder.actionId) : null; + traceId = builder.traceId != null ? UUID.fromString(builder.traceId) : null; + recipientId = builder.recipientId; + sourceContext = builder.sourceContext; + sourceType = builder.sourceType; + startDate = builder.startDate; + if ((endDate = builder.endDate) != null && startDate != null && startDate.isAfter(endDate)) { + throw new IllegalStateException("Start date cannot be later than end date."); + } + } + + RequestBuilder addParams(RequestBuilder request) { + request.addParameter("page", "1").addParameter("page_size", "1000"); + if (order != null) { + request.addParameter("order", order.toString()); + } + if (runId != null) { + request.addParameter("run_id", runId.toString()); + } + if (runItemId != null) { + request.addParameter("run_item_id", runItemId.toString()); + } + if (invocationId != null) { + request.addParameter("invocation_id", invocationId.toString()); + } + if (actionId != null) { + request.addParameter("action_id", actionId.toString()); + } + if (traceId != null) { + request.addParameter("trace_id", traceId.toString()); + } + if (recipientId != null) { + request.addParameter("recipient_id", recipientId); + } + if (sourceContext != null) { + request.addParameter("src_ctx", sourceContext); + } + if (sourceType != null) { + request.addParameter("src_type", sourceType.toString()); + } + if (startDate != null) { + request.addParameter("date_start", startDate.truncatedTo(ChronoUnit.SECONDS).toString()); + } + if (endDate != null) { + request.addParameter("date_end", endDate.truncatedTo(ChronoUnit.SECONDS).toString()); + } + return request; + } + + /** + * Sort in either ascending or descending order. + * + * @return The sort order as an enum, or {@code null} if unspecified. + */ + public SortOrder getOrder() { + return order; + } + + /** + * Run ID to filter by. + * + * @return The run ID, or {@code null} if unspecified. + */ + public UUID getRunId() { + return runId; + } + + /** + * Run item ID to filter by. + * + * @return The run item ID, or {@code null} if unspecified. + */ + public UUID getRunItemId() { + return runItemId; + } + + /** + * Invocation ID to filter by. + * + * @return The invocation ID, or {@code null} if unspecified. + */ + public UUID getInvocationId() { + return invocationId; + } + + /** + * Action ID to filter by. + * + * @return The action ID, or {@code null} if unspecified. + */ + public UUID getActionId() { + return actionId; + } + + /** + * Trace ID to filter by. + * + * @return The trace ID, or {@code null} if unspecified. + */ + public UUID getTraceId() { + return traceId; + } + + /** + * Recipient ID to filter by. + * + * @return The recipient ID, or {@code null} if unspecified. + */ + public String getRecipientId() { + return recipientId; + } + + /** + * The name of the segment / matcher the item / event to filter by (exact string). + * + * @return The source context string, or {@code null} if unspecified. + */ + public String getSourceContext() { + return sourceContext; + } + + /** + * Source type to filter by. + * + * @return The source type as an enum, or {@code null} if unspecified. + */ + public SourceType getSourceType() { + return sourceType; + } + + /** + * ISO-8601 formatted date for when to begin events filter. + * + * @return The start date for events as an Instant, or {@code null} if unspecified. + */ + public Instant getStartDate() { + return startDate; + } + + /** + * ISO-8601 formatted date for when to end events filter. + * + * @return The end date for events as an Instant, or {@code null} if unspecified. + */ + public Instant getEndDate() { + return endDate; + } + + /** + * Entry point for constructing an instance of this class. + * + * @return A new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for specifying options to filter events by. All fields are optional. + */ + public static class Builder { + private SortOrder order; + private String runId, runItemId, invocationId, actionId, traceId, recipientId, sourceContext; + private SourceType sourceType; + private Instant startDate, endDate; + + Builder() {} + + /** + * Sort in either ascending or descending order. + * + * @param order The sort order as an enum. + * + * @return This builder. + */ + public Builder order(SortOrder order) { + this.order = order; + return this; + } + + /** + * Run ID to filter by. + * + * @param runId The run ID. + * + * @return This builder. + */ + public Builder runId(String runId) { + this.runId = runId; + return this; + } + + /** + * Run item ID to filter by. + * + * @param runItemId The run item ID. + * + * @return This builder. + */ + public Builder runItemId(String runItemId) { + this.runItemId = runItemId; + return this; + } + + /** + * Invocation ID to filter by. + * + * @param invocationId The invocation ID. + * + * @return This builder. + */ + public Builder invocationId(String invocationId) { + this.invocationId = invocationId; + return this; + } + + /** + * Action ID to filter by. + * + * @param actionId The action ID. + * + * @return This builder. + */ + public Builder actionId(String actionId) { + this.actionId = actionId; + return this; + } + + /** + * Trace ID to filter by. + * + * @param traceId The trace ID. + * + * @return This builder. + */ + public Builder traceId(String traceId) { + this.traceId = traceId; + return this; + } + + /** + * Recipient ID to filter by. + * + * @param recipientId The recipient ID. + * + * @return This builder. + */ + public Builder recipientId(String recipientId) { + this.recipientId = recipientId; + return this; + } + + /** + * The name of the segment / matcher the item / event to filter by (exact string). + * + * @param sourceContext The source context string. + * + * @return This builder. + */ + public Builder sourceContext(String sourceContext) { + this.sourceContext = sourceContext; + return this; + } + + /** + * Source type to filter by. + * + * @param sourceType The source type as an enum. + * + * @return This builder. + */ + public Builder sourceType(SourceType sourceType) { + this.sourceType = sourceType; + return this; + } + + /** + * ISO-8601 formatted date for when to begin events filter. + * + * @param startDate The start date for events as an Instant. + * + * @return This builder. + */ + public Builder startDate(Instant startDate) { + this.startDate = startDate; + return this; + } + + /** + * ISO-8601 formatted date for when to end events filter. + * + * @param endDate The end date for events as an Instant. + * + * @return This builder. + */ + public Builder endDate(Instant endDate) { + this.endDate = endDate; + return this; + } + + /** + * Builds the {@linkplain ListEventsFilter}. + * + * @return An instance of ListEventsFilter, populated with all fields from this builder. + */ + public ListEventsFilter build() { + return new ListEventsFilter(this); + } + } +} diff --git a/src/main/java/com/vonage/client/proactiveconnect/ListEventsResponse.java b/src/main/java/com/vonage/client/proactiveconnect/ListEventsResponse.java index b32796c3c..50bf8dfda 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/ListEventsResponse.java +++ b/src/main/java/com/vonage/client/proactiveconnect/ListEventsResponse.java @@ -26,10 +26,10 @@ import java.util.List; /** - * HAL response for {@link ProactiveConnectClient#listEvents(int, int)}. + * HAL response for {@link ProactiveConnectClient#listEvents(ListEventsFilter)}. */ @JsonIgnoreProperties(ignoreUnknown = true) -public final class ListEventsResponse extends HalPageResponse { +final class ListEventsResponse extends HalPageResponse { @JsonProperty("_embedded") private Embedded _embedded; ListEventsResponse() { diff --git a/src/main/java/com/vonage/client/proactiveconnect/ListItemsResponse.java b/src/main/java/com/vonage/client/proactiveconnect/ListItemsResponse.java index 570ac8a38..897923b8e 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/ListItemsResponse.java +++ b/src/main/java/com/vonage/client/proactiveconnect/ListItemsResponse.java @@ -27,7 +27,7 @@ import java.util.UUID; /** - * HAL response for {@link ProactiveConnectClient#listItems(UUID, int, int)}. + * HAL response for {@link ProactiveConnectClient#listItems(UUID, int, int, SortOrder)}. */ @JsonIgnoreProperties(ignoreUnknown = true) public final class ListItemsResponse extends HalPageResponse { diff --git a/src/main/java/com/vonage/client/proactiveconnect/ListListsEndpoint.java b/src/main/java/com/vonage/client/proactiveconnect/ListListsEndpoint.java index f9ad38b30..4e634f8af 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/ListListsEndpoint.java +++ b/src/main/java/com/vonage/client/proactiveconnect/ListListsEndpoint.java @@ -22,7 +22,7 @@ import org.apache.http.client.methods.RequestBuilder; import java.io.IOException; -class ListListsEndpoint extends AbstractMethod { +class ListListsEndpoint extends AbstractMethod { private static final Class[] ALLOWED_AUTH_METHODS = {JWTAuthMethod.class}; private static final String PATH = "/v0.1/bulk/lists"; @@ -43,10 +43,10 @@ public RequestBuilder makeRequest(HalRequestWrapper request) { } @Override - public ListsResponse parseResponse(HttpResponse response) throws IOException { + public ListListsResponse parseResponse(HttpResponse response) throws IOException { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode >= 200 && statusCode < 300) { - return ListsResponse.fromJson(basicResponseHandler.handleResponse(response)); + return ListListsResponse.fromJson(basicResponseHandler.handleResponse(response)); } else { throw ProactiveConnectResponseException.fromHttpResponse(response); diff --git a/src/main/java/com/vonage/client/proactiveconnect/ListsResponse.java b/src/main/java/com/vonage/client/proactiveconnect/ListListsResponse.java similarity index 89% rename from src/main/java/com/vonage/client/proactiveconnect/ListsResponse.java rename to src/main/java/com/vonage/client/proactiveconnect/ListListsResponse.java index 7d21e5dd3..cff0e254c 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/ListsResponse.java +++ b/src/main/java/com/vonage/client/proactiveconnect/ListListsResponse.java @@ -26,13 +26,13 @@ import java.util.List; /** - * HAL response for {@link ProactiveConnectClient#listLists(int, int)}. + * HAL response for {@link ProactiveConnectClient#listLists(int, int, SortOrder)}. */ @JsonIgnoreProperties(ignoreUnknown = true) -public final class ListsResponse extends HalPageResponse { +public final class ListListsResponse extends HalPageResponse { @JsonProperty("_embedded") private Embedded _embedded; - ListsResponse() {} + ListListsResponse() {} @JsonIgnoreProperties(ignoreUnknown = true) static final class Embedded { @@ -60,14 +60,14 @@ public List getLists() { * @param json The JSON string to parse. * @return An instance of this class with the fields populated, if present. */ - public static ListsResponse fromJson(String json) { + public static ListListsResponse fromJson(String json) { try { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); - return mapper.readValue(json, ListsResponse.class); + return mapper.readValue(json, ListListsResponse.class); } catch (IOException ex) { - throw new VonageResponseParseException("Failed to produce ListsResponse from json.", ex); + throw new VonageResponseParseException("Failed to produce ListListsResponse from json.", ex); } } } diff --git a/src/main/java/com/vonage/client/proactiveconnect/ProactiveConnectClient.java b/src/main/java/com/vonage/client/proactiveconnect/ProactiveConnectClient.java index e86681971..4ead91b7c 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/ProactiveConnectClient.java +++ b/src/main/java/com/vonage/client/proactiveconnect/ProactiveConnectClient.java @@ -76,14 +76,15 @@ private String validateUuid(String name, UUID uuid) { return Objects.requireNonNull(uuid, name+" is required.").toString(); } - private R halRequest(AbstractMethod endpoint, String id, Integer page, Integer pageSize) { + private R halRequest(AbstractMethod endpoint, + String id, Integer page, Integer pageSize, SortOrder order) { if (page != null && page < 1) { throw new IllegalArgumentException("Page number must be positive."); } if (pageSize != null && pageSize < 1) { throw new IllegalArgumentException("Page size must be positive."); } - return endpoint.execute(new HalRequestWrapper(page, pageSize, id)); + return endpoint.execute(new HalRequestWrapper(page, pageSize, order, id)); } /** @@ -182,21 +183,7 @@ public void fetchList(UUID listId) { * @throws ProactiveConnectResponseException If there was an error in retrieving the lists. */ public List listLists() { - return halRequest(listLists, null, 1, 1000).getLists(); - } - - /** - * Get all lists on a particular page (with the default number of lists per page). - * - * @param page The page number of the HAL response to parse results. - * - * @return The lists page. - * @see #listLists(int, int) - * - * @throws ProactiveConnectResponseException If there was an error in retrieving the lists. - */ - public ListsResponse listLists(int page) { - return halRequest(listLists, null,page, null); + return halRequest(listLists, null, 1, 1000, null).getLists(); } /** @@ -204,13 +191,14 @@ public ListsResponse listLists(int page) { * * @param page The page number of the HAL response to parse results. * @param pageSize Number of results per page in the HAL response. + * @param order The order to sort results by (ascending or descending). * * @return The lists page. * * @throws ProactiveConnectResponseException If there was an error in retrieving the lists. */ - public ListsResponse listLists(int page, int pageSize) { - return halRequest(listLists, null, page, pageSize); + public ListListsResponse listLists(int page, int pageSize, SortOrder order) { + return halRequest(listLists, null, page, pageSize, order); } /** @@ -360,22 +348,7 @@ public UploadListItemsResponse uploadListItems(UUID listId, Path csvFile) { * @throws ProactiveConnectResponseException If the list does not exist or the items couldn't be retrieved. */ public List listItems(UUID listId) { - return halRequest(listItems, validateUuid("List ID", listId), 1, 1000).getItems(); - } - - /** - * Get all items on a particular page (with the default number of items per page). - * - * @param listId Unique ID of the list to retrieve items from. - * @param page The page number of the HAL response to parse results. - * - * @return The items page. - * @see #listItems(UUID, int, int) - * - * @throws ProactiveConnectResponseException If the list does not exist or the items couldn't be retrieved. - */ - public ListItemsResponse listItems(UUID listId, int page) { - return halRequest(listItems, validateUuid("List ID", listId), page, null); + return halRequest(listItems, validateUuid("List ID", listId), 1, 1000, null).getItems(); } /** @@ -384,51 +357,26 @@ public ListItemsResponse listItems(UUID listId, int page) { * @param listId Unique ID of the list to retrieve items from. * @param page The page number of the HAL response to parse results. * @param pageSize Number of results per page in the HAL response. + * @param order The order to sort results by (ascending or descending). * * @return The items page. * * @throws ProactiveConnectResponseException If the list does not exist or the items couldn't be retrieved. */ - public ListItemsResponse listItems(UUID listId, int page, int pageSize) { - return halRequest(listItems, validateUuid("List ID", listId), page, pageSize); + public ListItemsResponse listItems(UUID listId, int page, int pageSize, SortOrder order) { + return halRequest(listItems, validateUuid("List ID", listId), page, pageSize, order); } /** - * Gets the first 1000 events in the application. - * - * @return The events in order of creation. - * - * @throws ProactiveConnectResponseException If the events couldn't be retrieved. - */ - public List listEvents() { - return halRequest(listEvents, null, 1, 1000).getEvents(); - } - - /** - * Get all events on a particular page (with the default number of events per page). - * - * @param page The page number of the HAL response to parse results. - * - * @return The events page. - * @see #listEvents(int, int) - * - * @throws ProactiveConnectResponseException If the events couldn't be retrieved. - */ - public ListEventsResponse listEvents(int page) { - return halRequest(listEvents, null, page, null); - } - - /** - * Get all events on a particular page. + * Gets all events in the application matching the criteria. * - * @param page The page number of the HAL response to parse results. - * @param pageSize Number of results per page in the HAL response. + * @param filter Optional attributes to narrow down the results. * - * @return The events page. + * @return The list of events applicable to the request criteria, in order of creation. * * @throws ProactiveConnectResponseException If the events couldn't be retrieved. */ - public ListEventsResponse listEvents(int page, int pageSize) { - return halRequest(listEvents, null, page, pageSize); + public List listEvents(ListEventsFilter filter) { + return listEvents.execute(filter != null ? filter : ListEventsFilter.builder().build()).getEvents(); } } diff --git a/src/main/java/com/vonage/client/proactiveconnect/SortOrder.java b/src/main/java/com/vonage/client/proactiveconnect/SortOrder.java new file mode 100644 index 000000000..51e0afa72 --- /dev/null +++ b/src/main/java/com/vonage/client/proactiveconnect/SortOrder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Vonage + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vonage.client.proactiveconnect; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Represents the sort order for events. + */ +public enum SortOrder { + /** + * Ascending + */ + ASC, + + /** + * Descending + */ + DESC; + + @JsonValue + @Override + public String toString() { + return name().toLowerCase(); + } +} diff --git a/src/main/java/com/vonage/client/proactiveconnect/SourceType.java b/src/main/java/com/vonage/client/proactiveconnect/SourceType.java new file mode 100644 index 000000000..8fb7c8cf7 --- /dev/null +++ b/src/main/java/com/vonage/client/proactiveconnect/SourceType.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Vonage + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.vonage.client.proactiveconnect; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Represents source types for events. + */ +public enum SourceType { + /** + * Segmentation + */ + SEGMENTATION, + + /** + * Event handler + */ + EVENT_HANDLER; + + @JsonValue + @Override + public String toString() { + return name().toLowerCase().replace('_', '-'); + } +} diff --git a/src/main/java/com/vonage/client/proactiveconnect/package-info.java b/src/main/java/com/vonage/client/proactiveconnect/package-info.java index da4466564..71435c385 100644 --- a/src/main/java/com/vonage/client/proactiveconnect/package-info.java +++ b/src/main/java/com/vonage/client/proactiveconnect/package-info.java @@ -16,8 +16,9 @@ /** * This package contains classes and sub-packages to support usage of the - * Vonage Proactive Connect API. - *
+ * Vonage Proactive Connect API. Please + * refer to the developer documentation + * for an overview of the concepts. * * @since 7.6.0 */ diff --git a/src/test/java/com/vonage/client/BugRepro.java b/src/test/java/com/vonage/client/BugRepro.java index 1d0c36512..b357c54c2 100644 --- a/src/test/java/com/vonage/client/BugRepro.java +++ b/src/test/java/com/vonage/client/BugRepro.java @@ -35,7 +35,7 @@ public static void main(String[] args) throws Throwable { try { // Debug code here - + System.out.println("Success"); } catch (Exception ex) { diff --git a/src/test/java/com/vonage/client/proactiveconnect/ListEventsEndpointTest.java b/src/test/java/com/vonage/client/proactiveconnect/ListEventsEndpointTest.java index 775ad8314..effbe4e93 100644 --- a/src/test/java/com/vonage/client/proactiveconnect/ListEventsEndpointTest.java +++ b/src/test/java/com/vonage/client/proactiveconnect/ListEventsEndpointTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; +import java.time.Instant; import java.util.List; import java.util.Map; @@ -47,24 +48,54 @@ public void testAuthMethod() { } @Test - public void testMakeRequestAllParams() throws Exception { - HalRequestWrapper request = new HalRequestWrapper(6, 50, null); + public void testDefaultUriAllParams() throws Exception { + ListEventsFilter request = ListEventsFilter.builder() + .order(SortOrder.ASC) + .runId("51aca838-2cf6-4100-b0d2-e74ac0e95c88") + .runItemId("d6f1d012-227e-4025-a388-3d1aaa05bc29") + .invocationId("29bab76c-156e-42e4-ab38-f6a465f0048e") + .actionId("99ea10e0-1f14-4f55-b976-3c88ea8ec4cd") + .traceId("a3c7472d-495a-4f6f-b29a-1646ffe70643") + .recipientId("15551584817") + .sourceContext("uk-customers") + .sourceType(SourceType.EVENT_HANDLER) + .startDate(Instant.EPOCH) + .endDate(Instant.parse("2023-06-29T12:39:34.319723Z")) + .build(); + RequestBuilder builder = endpoint.makeRequest(request); assertEquals("GET", builder.getMethod()); - String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/events" + - "?page=" + request.page + "&page_size=" + request.pageSize; - assertEquals(expectedUri, builder.build().getURI().toString()); + String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/events?"; + assertTrue(builder.build().getURI().toString().startsWith(expectedUri)); assertEquals(ContentType.APPLICATION_JSON.getMimeType(), builder.getFirstHeader("Accept").getValue()); + Map params = TestUtils.makeParameterMap(builder.getParameters()); - assertEquals(2, params.size()); - } + assertEquals(13, params.size()); + assertEquals("1", params.get("page")); + assertEquals("1000", params.get("page_size")); + assertEquals("asc", params.get("order")); + assertEquals("51aca838-2cf6-4100-b0d2-e74ac0e95c88", params.get("run_id")); + assertEquals("d6f1d012-227e-4025-a388-3d1aaa05bc29", params.get("run_item_id")); + assertEquals("29bab76c-156e-42e4-ab38-f6a465f0048e", params.get("invocation_id")); + assertEquals("99ea10e0-1f14-4f55-b976-3c88ea8ec4cd", params.get("action_id")); + assertEquals("a3c7472d-495a-4f6f-b29a-1646ffe70643", params.get("trace_id")); + assertEquals("15551584817", params.get("recipient_id")); + assertEquals("uk-customers", params.get("src_ctx")); + assertEquals("event-handler", params.get("src_type")); + assertEquals("1970-01-01T00:00:00Z", params.get("date_start")); + assertEquals("2023-06-29T12:39:34Z", params.get("date_end")); - @Test - public void testDefaultUri() throws Exception { - RequestBuilder builder = endpoint.makeRequest(new HalRequestWrapper(null, null, null)); - assertEquals("GET", builder.getMethod()); - String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/events"; - assertEquals(expectedUri, builder.build().getURI().toString()); + assertNotNull(request.getOrder()); + assertNotNull(request.getActionId()); + assertNotNull(request.getInvocationId()); + assertNotNull(request.getRecipientId()); + assertNotNull(request.getRunItemId()); + assertNotNull(request.getRunId()); + assertNotNull(request.getTraceId()); + assertNotNull(request.getSourceContext()); + assertNotNull(request.getSourceType()); + assertNotNull(request.getStartDate()); + assertNotNull(request.getEndDate()); } @Test @@ -72,9 +103,8 @@ public void testCustomUri() throws Exception { String baseUri = "http://example.com"; HttpWrapper wrapper = new HttpWrapper(HttpConfig.builder().baseUri(baseUri).build()); endpoint = new ListEventsEndpoint(wrapper); - HalRequestWrapper request = new HalRequestWrapper(3, null, null); - String expectedUri = baseUri + "/v0.1/bulk/events?page="+ request.page; - RequestBuilder builder = endpoint.makeRequest(request); + String expectedUri = baseUri + "/v0.1/bulk/events?page=1&page_size=1000"; + RequestBuilder builder = endpoint.makeRequest(ListEventsFilter.builder().build()); assertEquals(expectedUri, builder.build().getURI().toString()); assertEquals(ContentType.APPLICATION_JSON.getMimeType(), builder.getFirstHeader("Accept").getValue()); assertEquals("GET", builder.getMethod()); diff --git a/src/test/java/com/vonage/client/proactiveconnect/ListItemsEndpointTest.java b/src/test/java/com/vonage/client/proactiveconnect/ListItemsEndpointTest.java index 907837367..f41fd7bb7 100644 --- a/src/test/java/com/vonage/client/proactiveconnect/ListItemsEndpointTest.java +++ b/src/test/java/com/vonage/client/proactiveconnect/ListItemsEndpointTest.java @@ -50,24 +50,24 @@ public void testAuthMethod() { @Test public void testMakeRequestAllParams() throws Exception { - HalRequestWrapper request = new HalRequestWrapper(7, 30, listId); + HalRequestWrapper request = new HalRequestWrapper(7, 30, SortOrder.DESC, listId); RequestBuilder builder = endpoint.makeRequest(request); assertEquals("GET", builder.getMethod()); String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/lists/"+request.id+"/items" + - "?page=" + request.page + "&page_size=" +request.pageSize; + "?page="+request.page+"&page_size="+request.pageSize+"&order="+request.order; assertEquals(expectedUri, builder.build().getURI().toString()); assertEquals(ContentType.APPLICATION_JSON.getMimeType(), builder.getFirstHeader("Accept").getValue()); Map params = TestUtils.makeParameterMap(builder.getParameters()); - assertEquals(2, params.size()); - String expectedResponse = "{}"; - HttpResponse mockResponse = TestUtils.makeJsonHttpResponse(200, expectedResponse); + assertEquals(3, params.size()); + HttpResponse mockResponse = TestUtils.makeJsonHttpResponse(200, "{}"); ListItemsResponse parsed = endpoint.parseResponse(mockResponse); assertNotNull(parsed); } @Test public void testDefaultUri() throws Exception { - RequestBuilder builder = endpoint.makeRequest(new HalRequestWrapper(null, null, listId)); + HalRequestWrapper request = new HalRequestWrapper(null, null, null, listId); + RequestBuilder builder = endpoint.makeRequest(request); assertEquals("GET", builder.getMethod()); String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/lists/"+listId+"/items"; assertEquals(expectedUri, builder.build().getURI().toString()); @@ -78,7 +78,7 @@ public void testCustomUri() throws Exception { String baseUri = "http://example.com"; HttpWrapper wrapper = new HttpWrapper(HttpConfig.builder().baseUri(baseUri).build()); endpoint = new ListItemsEndpoint(wrapper); - HalRequestWrapper request = new HalRequestWrapper(2, null, listId); + HalRequestWrapper request = new HalRequestWrapper(2, null, null, listId); String expectedUri = baseUri + "/v0.1/bulk/lists/"+request.id+"/items?page="+request.page; RequestBuilder builder = endpoint.makeRequest(request); assertEquals(expectedUri, builder.build().getURI().toString()); diff --git a/src/test/java/com/vonage/client/proactiveconnect/ListListsEndpointTest.java b/src/test/java/com/vonage/client/proactiveconnect/ListListsEndpointTest.java index c04b37108..23eb9ceeb 100644 --- a/src/test/java/com/vonage/client/proactiveconnect/ListListsEndpointTest.java +++ b/src/test/java/com/vonage/client/proactiveconnect/ListListsEndpointTest.java @@ -48,20 +48,21 @@ public void testAuthMethod() { @Test public void testMakeRequestAllParams() throws Exception { - HalRequestWrapper request = new HalRequestWrapper(3, 12, null); + HalRequestWrapper request = new HalRequestWrapper(3, 12, SortOrder.DESC, null); RequestBuilder builder = endpoint.makeRequest(request); assertEquals("GET", builder.getMethod()); String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/lists" + - "?page=" + request.page + "&page_size=" + request.pageSize; + "?page=" + request.page + "&page_size=" + request.pageSize + "&order=" + request.order; assertEquals(expectedUri, builder.build().getURI().toString()); assertEquals(ContentType.APPLICATION_JSON.getMimeType(), builder.getFirstHeader("Accept").getValue()); Map params = TestUtils.makeParameterMap(builder.getParameters()); - assertEquals(2, params.size()); + assertEquals(3, params.size()); } @Test public void testDefaultUri() throws Exception { - RequestBuilder builder = endpoint.makeRequest(new HalRequestWrapper(null, null, null)); + HalRequestWrapper request = new HalRequestWrapper(null, null, null, null); + RequestBuilder builder = endpoint.makeRequest(request); assertEquals("GET", builder.getMethod()); String expectedUri = "https://api-eu.vonage.com/v0.1/bulk/lists"; assertEquals(expectedUri, builder.build().getURI().toString()); @@ -72,7 +73,7 @@ public void testCustomUri() throws Exception { String baseUri = "http://example.com"; HttpWrapper wrapper = new HttpWrapper(HttpConfig.builder().baseUri(baseUri).build()); endpoint = new ListListsEndpoint(wrapper); - HalRequestWrapper request = new HalRequestWrapper(5, null, null); + HalRequestWrapper request = new HalRequestWrapper(5, null, null, null); String expectedUri = baseUri + "/v0.1/bulk/lists?page=" + request.page; RequestBuilder builder = endpoint.makeRequest(request); assertEquals(expectedUri, builder.build().getURI().toString()); @@ -83,7 +84,7 @@ public void testCustomUri() throws Exception { @Test public void testEmptyResponse() throws Exception { HttpResponse mockResponse = TestUtils.makeJsonHttpResponse(200, "{}"); - ListsResponse parsed = endpoint.parseResponse(mockResponse); + ListListsResponse parsed = endpoint.parseResponse(mockResponse); assertNotNull(parsed); assertNull(parsed.getLists()); assertNull(parsed.getLinks()); @@ -121,7 +122,7 @@ public void testFullResponse() throws Exception { " }\n" + "}"; HttpResponse mockResponse = TestUtils.makeJsonHttpResponse(200, expectedResponse); - ListsResponse parsed = endpoint.parseResponse(mockResponse); + ListListsResponse parsed = endpoint.parseResponse(mockResponse); assertNotNull(parsed); HalLinks links = parsed.getLinks(); assertNotNull(links); @@ -141,7 +142,7 @@ public void testFullResponse() throws Exception { @Test(expected = VonageResponseParseException.class) public void testInvalidResponse() { - ListsResponse.fromJson("{malformed]"); + ListListsResponse.fromJson("{malformed]"); } @Test(expected = ProactiveConnectResponseException.class) diff --git a/src/test/java/com/vonage/client/proactiveconnect/ProactiveConnectClientTest.java b/src/test/java/com/vonage/client/proactiveconnect/ProactiveConnectClientTest.java index d93d3e6bf..f252e33bd 100644 --- a/src/test/java/com/vonage/client/proactiveconnect/ProactiveConnectClientTest.java +++ b/src/test/java/com/vonage/client/proactiveconnect/ProactiveConnectClientTest.java @@ -27,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.time.Instant; import java.util.*; @@ -287,19 +288,10 @@ static void assertNullEvent(Event response) { @Test public void testCreateList() throws Exception { ContactsList request = ContactsList.builder("my list").build(); - assertNull(request.getDatasource()); - assertNull(request.getCreatedAt()); - assertNull(request.getUpdatedAt()); - assertNull(request.getTags()); - assertNull(request.getAttributes()); - assertNull(request.getDescription()); - assertNull(request.getId()); - assertNull(request.getSyncStatus()); - assertNull(request.getItemsCount()); - assertNotNull(request.getName()); assert409ResponseException(() -> client.createList(request)); - stubResponseAndRun(SAMPLE_LIST_RESPONSE, () -> client.createList(request)); - assertEqualsSampleList(request); + ContactsList response = stubResponseAndGet(SAMPLE_LIST_RESPONSE, () -> client.createList(request)); + assertEquals(request, response); + assertEqualsSampleList(response); stubResponseAndAssertThrows(201, () -> client.createList(null), NullPointerException.class); stubResponseAndAssertThrows(201, () -> client.createList(ContactsList.builder(null).build()), @@ -369,7 +361,7 @@ public void testFetchList() throws Exception { @Test public void testListLists() throws Exception { stubResponse(HAL_TEMPLATE_RESPONSE + " \"lists\": [{},"+SAMPLE_LIST_RESPONSE+",{}]\n} \n}"); - ListsResponse parsed = client.listLists(2, 10); + ListListsResponse parsed = client.listLists(2, 10, SortOrder.ASC); assertEqualsSampleHal(parsed); List lists = parsed.getLists(); assertNotNull(lists); @@ -377,12 +369,12 @@ public void testListLists() throws Exception { assertNullList(lists.get(0)); assertEqualsSampleList(lists.get(1)); assertNullList(lists.get(2)); - assertNull(stubResponseAndGet("{}", () -> client.listLists(1, 1)).getLists()); + assertNull(stubResponseAndGet("{}", () -> client.listLists(1, 1, null)).getLists()); stubResponseAndAssertThrows("{}", () -> - client.listLists(0), IllegalArgumentException.class + client.listLists(1, 0, SortOrder.ASC), IllegalArgumentException.class ); stubResponseAndAssertThrows("{}", () -> - client.listLists(1, 0), IllegalArgumentException.class + client.listLists(0, 25, SortOrder.DESC), IllegalArgumentException.class ); assertNull(stubResponseAndGet("{}", client::listLists)); String emptyListsResponse = "{\"_embedded\":{\"lists\":[]}}"; @@ -393,9 +385,9 @@ public void testListLists() throws Exception { @Test public void testCreateListItem() throws Exception { Map data = Collections.emptyMap(); - stubResponseAndRun(SAMPLE_LIST_ITEM_RESPONSE, () -> + assertEqualsSampleListItem(stubResponseAndGet(SAMPLE_LIST_ITEM_RESPONSE, () -> client.createListItem(SAMPLE_LIST_ID, data) - ); + )); stubResponseAndAssertThrows(200, () -> client.createListItem(null, data), NullPointerException.class ); @@ -504,7 +496,7 @@ public void testUploadListItems() throws Exception { @Test public void testListItems() throws Exception { stubResponse(HAL_TEMPLATE_RESPONSE + " \"items\": [{},"+ SAMPLE_LIST_ITEM_RESPONSE +",{}]\n} \n}"); - ListItemsResponse parsed = client.listItems(SAMPLE_LIST_ID, 2, 10); + ListItemsResponse parsed = client.listItems(SAMPLE_LIST_ID, 2, 10, SortOrder.ASC); assertEqualsSampleHal(parsed); List items = parsed.getItems(); assertNotNull(items); @@ -512,21 +504,20 @@ public void testListItems() throws Exception { assertNullListItem(items.get(0)); assertEqualsSampleListItem(items.get(1)); assertNullListItem(items.get(2)); - assertNull(stubResponseAndGet("{}", () -> client.listItems(SAMPLE_LIST_ID, 1, 1).getItems())); + assertNull(stubResponseAndGet("{}", () -> + client.listItems(SAMPLE_LIST_ID, 1, 1, null).getItems() + )); stubResponseAndAssertThrows("{}", () -> - client.listItems(SAMPLE_LIST_ID, 1, 0), IllegalArgumentException.class + client.listItems(SAMPLE_LIST_ID, 1, 0, SortOrder.DESC), IllegalArgumentException.class ); stubResponseAndAssertThrows("{}", () -> - client.listItems(SAMPLE_LIST_ID, 0), IllegalArgumentException.class + client.listItems(SAMPLE_LIST_ID, 0, 25, SortOrder.DESC), IllegalArgumentException.class ); stubResponseAndAssertThrows("{}", () -> client.listItems(null), NullPointerException.class ); stubResponseAndAssertThrows("{}", () -> - client.listItems(null, 3, 25), NullPointerException.class - ); - stubResponseAndAssertThrows("{}", () -> - client.listItems(null, 2), NullPointerException.class + client.listItems(null, 3, 25, SortOrder.ASC), NullPointerException.class ); assertNull(stubResponseAndGet("{}", () -> client.listItems(SAMPLE_LIST_ID))); String emptyListsResponse = "{\"_embedded\":{\"items\":[]}}"; @@ -537,22 +528,23 @@ public void testListItems() throws Exception { @Test public void testListEvents() throws Exception { stubResponse(HAL_TEMPLATE_RESPONSE + " \"events\": [{},"+ SAMPLE_EVENT_RESPONSE +",{}]\n} \n}"); - ListEventsResponse parsed = client.listEvents(2, 10); - assertEqualsSampleHal(parsed); - List events = parsed.getEvents(); + List events = client.listEvents(null); assertNotNull(events); assertEquals(3, events.size()); assertNullEvent(events.get(0)); assertEqualsSampleEvent(events.get(1)); assertNullEvent(events.get(2)); - assertNull(stubResponseAndGet("{}", () -> client.listEvents(1, 1)).getEvents()); + assertNull(stubResponseAndGet("{}", () -> client.listEvents(null))); stubResponseAndAssertThrows("{}", () -> - client.listEvents(1, 0), IllegalArgumentException.class + client.listEvents(ListEventsFilter.builder() + .startDate(Instant.now()) + .endDate(Instant.now().minus(Duration.ofHours(3))) + .build() + ), + IllegalStateException.class ); - stubResponseAndAssertThrows("{}", () -> client.listEvents(0), IllegalArgumentException.class); - assertNull(stubResponseAndGet("{}", client::listEvents)); String emptyListsResponse = "{\"_embedded\":{\"events\":[]}}"; - assertEquals(0, stubResponseAndGet(emptyListsResponse, client::listEvents).size()); - assert409ResponseException(client::listEvents); + assertEquals(0, stubResponseAndGet(emptyListsResponse, () -> client.listEvents(null)).size()); + assert409ResponseException(() -> client.listEvents(ListEventsFilter.builder().build())); } } \ No newline at end of file