001/*
002 *   Copyright 2024 Vonage
003 *
004 *   Licensed under the Apache License, Version 2.0 (the "License");
005 *   you may not use this file except in compliance with the License.
006 *   You may obtain a copy of the License at
007 *
008 *        http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *   Unless required by applicable law or agreed to in writing, software
011 *   distributed under the License is distributed on an "AS IS" BASIS,
012 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *   See the License for the specific language governing permissions and
014 *   limitations under the License.
015 */
016package com.vonage.client.proactiveconnect;
017
018import com.vonage.client.*;
019import com.vonage.client.auth.JWTAuthMethod;
020import com.vonage.client.common.HalPageResponse;
021import com.vonage.client.common.HttpMethod;
022import java.io.IOException;
023import java.nio.file.Files;
024import java.nio.file.Path;
025import java.util.List;
026import java.util.Map;
027import java.util.Objects;
028import java.util.UUID;
029import java.util.function.Function;
030
031/**
032 * A client for talking to the Vonage Proactive Connect API. The standard way to obtain an instance
033 * of this class is to use {@link VonageClient#getProactiveConnectClient()}.
034 *
035 * @deprecated This API is sunset and will be removed in the next major release.
036 */
037@Deprecated
038public class ProactiveConnectClient {
039        final RestEndpoint<ContactsList, ContactsList> createList, updateList;
040        final RestEndpoint<UUID, ContactsList> getList;
041        final RestEndpoint<UUID, Void> deleteList, clearList, fetchList;
042        final RestEndpoint<HalRequestWrapper, ListListsResponse> listLists;
043        final RestEndpoint<HalRequestWrapper, ListItemsResponse> listItems;
044        final RestEndpoint<ListItemRequestWrapper, ListItem> createListItem, getListItem, updateListItem;
045        final RestEndpoint<ListItemRequestWrapper, Void> deleteListItem;
046        final RestEndpoint<UUID, byte[]> downloadListItems;
047        final RestEndpoint<UploadListItemsRequestWrapper, UploadListItemsResponse> uploadListItems;
048        final RestEndpoint<ListEventsFilter, ListEventsResponse> listEvents;
049
050        /**
051         * Constructor.
052         *
053         * @param wrapper (REQUIRED) shared HTTP wrapper object used for making REST calls.
054         */
055        public ProactiveConnectClient(HttpWrapper wrapper) {
056
057                @SuppressWarnings("unchecked")
058                class Endpoint<T, R> extends DynamicEndpoint<T, R> {
059                        Endpoint(Function<T, String> pathGetter, HttpMethod method, R... type) {
060                                super(DynamicEndpoint.<T, R> builder(type).authMethod(JWTAuthMethod.class)
061                                                .responseExceptionType(ProactiveConnectResponseException.class)
062                                                .requestMethod(method).wrapper(wrapper).pathGetter((de, req) -> {
063                                                        String base = de.getHttpWrapper().getHttpConfig().getApiEuBaseUri();
064                                                        return base + "/v0.1/bulk/" + pathGetter.apply(req);
065                                                })
066                                );
067                        }
068                }
069
070                createList = new Endpoint<>(req -> "lists", HttpMethod.POST);
071                getList = new Endpoint<>(listId -> "lists/"+listId, HttpMethod.GET);
072                updateList = new Endpoint<>(list -> "lists/"+list.getId(), HttpMethod.PUT);
073                deleteList = new Endpoint<>(listId -> "lists/"+listId, HttpMethod.DELETE);
074                clearList = new Endpoint<>(listId -> "lists/"+listId+"/clear", HttpMethod.POST);
075                fetchList = new Endpoint<>(listId -> "lists/"+listId+"/fetch", HttpMethod.POST);
076                listLists = new Endpoint<>(req -> "lists", HttpMethod.GET);
077                listItems = new Endpoint<>(req -> "lists/"+req.id+"/items", HttpMethod.GET);
078                createListItem = new Endpoint<>(req -> "lists/"+req.listId+"/items", HttpMethod.POST);
079                getListItem = new Endpoint<>(req -> "lists/"+req.listId+"/items/"+req.itemId, HttpMethod.GET);
080                updateListItem = new Endpoint<>(req -> "lists/"+req.listId+"/items/"+req.itemId, HttpMethod.PUT);
081                deleteListItem = new Endpoint<>(req -> "lists/"+req.listId+"/items/"+req.itemId, HttpMethod.DELETE);
082                downloadListItems = new Endpoint<>(listId -> "lists/"+listId+"/items/download", HttpMethod.GET);
083                uploadListItems = new Endpoint<>(req -> "lists/"+req.listId+"/items/import", HttpMethod.POST);
084                listEvents = new Endpoint<>(req -> "events", HttpMethod.GET);
085        }
086
087        private UUID validateUuid(String name, UUID uuid) {
088                return Objects.requireNonNull(uuid, name+" is required.");
089        }
090
091        private <R extends HalPageResponse> R halRequest(RestEndpoint<HalRequestWrapper, R> endpoint,
092                        String id, Integer page, Integer pageSize, SortOrder order) {
093                if (page != null && page < 1) {
094                        throw new IllegalArgumentException("Page number must be positive.");
095                }
096                if (pageSize != null && pageSize < 1) {
097                        throw new IllegalArgumentException("Page size must be positive.");
098                }
099                return endpoint.execute(new HalRequestWrapper(page, pageSize, order != null ? order.toSortOrder() : null, id));
100        }
101
102        /**
103         * Create a new list.
104         *
105         * @param list The new list's properties.
106         *
107         * @return The list that was created with updated metadata.
108         *
109         * @throws ProactiveConnectResponseException If the request was unsuccessful.
110         * This could be for the following reasons:
111         * <ul>
112         *   <li><b>400</b>: Invalid request parameter or body.</li>
113         *       <li><b>409</b>: Conflict.</li>
114         *       <li><b>422</b>: Resource limit reached / exceeded.</li>
115         * </ul>
116         */
117        public ContactsList createList(ContactsList list) {
118                return createList.execute(Objects.requireNonNull(list, "List structure is required."));
119        }
120
121        /**
122         * Retrieve a list.
123         *
124         * @param listId Unique ID of the list.
125         *
126         * @return The list associated with the ID.
127         *
128         * @throws ProactiveConnectResponseException If the list does not exist or couldn't be retrieved.
129         */
130        public ContactsList getList(UUID listId) {
131                return getList.execute(validateUuid("List ID", listId));
132        }
133
134        /**
135         * Update an existing list.
136         *
137         * @param listId Unique ID of the list.
138         * @param updatedList The new list properties.
139         *
140         * @return The updated list.
141         *
142         * @throws ProactiveConnectResponseException If the request was unsuccessful.
143         * This could be for the following reasons:
144         * <ul>
145         *   <li><b>400</b>: Invalid request parameter or body.</li>
146         *       <li><b>404</b>: List not found.</li>
147         *       <li><b>409</b>: Conflict.</li>
148         * </ul>
149         */
150        public ContactsList updateList(UUID listId, ContactsList updatedList) {
151                Objects.requireNonNull(updatedList, "List structure is required.");
152                updatedList.id = validateUuid("List ID", listId);
153                return updateList.execute(updatedList);
154        }
155
156        /**
157         * Delete a list.
158         *
159         * @param listId Unique ID of the list.
160         *
161         * @throws ProactiveConnectResponseException If the list does not exist or couldn't be deleted.
162         */
163        public void deleteList(UUID listId) {
164                deleteList.execute(validateUuid("List ID", listId));
165        }
166
167        /**
168         * Delete all items in a list.
169         *
170         * @param listId Unique ID of the list.
171         *
172         * @throws ProactiveConnectResponseException If the list does not exist or couldn't be cleared.
173         */
174        public void clearList(UUID listId) {
175                clearList.execute(validateUuid("List ID", listId));
176        }
177
178        /**
179         * Fetch and replace all items from datasource.
180         *
181         * @param listId Unique ID of the list.
182         *
183         * @throws ProactiveConnectResponseException If the list does not exist or couldn't be fetched.
184         */
185        public void fetchList(UUID listId) {
186                fetchList.execute(validateUuid("List ID", listId));
187        }
188
189        /**
190         * Gets the first 1000 lists in the application.
191         *
192         * @return The lists in order of creation.
193         *
194         * @throws ProactiveConnectResponseException If there was an error in retrieving the lists.
195         */
196        public List<ContactsList> listLists() {
197                return halRequest(listLists, null, 1, 1000, null).getLists();
198        }
199
200        /**
201         * Get all lists on a particular page.
202         *
203         * @param page The page number of the HAL response to parse results.
204         * @param pageSize Number of results per page in the HAL response.
205         * @param order The order to sort results by (ascending or descending).
206         *
207         * @return The lists page.
208         *
209         * @throws ProactiveConnectResponseException If there was an error in retrieving the lists.
210         */
211        public ListListsResponse listLists(int page, int pageSize, SortOrder order) {
212                return halRequest(listLists, null, page, pageSize, order);
213        }
214
215        /**
216         * Create a new list item.
217         *
218         * @param listId Unique ID of the list.
219         * @param data The new item's data as a Map.
220         *
221         * @return The created list item.
222         *
223         * @throws ProactiveConnectResponseException If the request was unsuccessful.
224         * This could be for the following reasons:
225         * <ul>
226         *   <li><b>400</b>: Invalid request parameter or body.</li>
227         *       <li><b>404</b>: List not found.</li>
228         *       <li><b>422</b>: Resource limit reached / exceeded.</li>
229         * </ul>
230         */
231        public ListItem createListItem(UUID listId, Map<String, ?> data) {
232                return createListItem.execute(new ListItemRequestWrapper(
233                                validateUuid("List ID", listId), null,
234                                Objects.requireNonNull(data, "List data is required.")
235                ));
236        }
237
238        /**
239         * Retrieve a list item.
240         *
241         * @param listId Unique ID of the list.
242         * @param itemId Unique ID of the item.
243         *
244         * @return The requested list item.
245         *
246         * @throws ProactiveConnectResponseException If the list or item does not exist or couldn't be retrieved.
247         */
248        public ListItem getListItem(UUID listId, UUID itemId) {
249                return getListItem.execute(new ListItemRequestWrapper(
250                                validateUuid("List ID", listId), validateUuid("Item ID", itemId), null
251                ));
252        }
253
254        /**
255         * Update an existing list item.
256         *
257         * @param listId Unique ID of the list.
258         * @param itemId Unique ID of the item.
259         * @param data The updated item data as a Map.
260         *
261         * @return The updated list item.
262         *
263         * @throws ProactiveConnectResponseException If the request was unsuccessful.
264         * This could be for the following reasons:
265         * <ul>
266         *   <li><b>400</b>: Invalid request parameter or body.</li>
267         *       <li><b>404</b>: List or item not found.</li>
268         * </ul>
269         */
270        public ListItem updateListItem(UUID listId, UUID itemId, Map<String, ?> data) {
271                return updateListItem.execute(new ListItemRequestWrapper(
272                                validateUuid("List ID", listId),
273                                validateUuid("Item ID", itemId),
274                                Objects.requireNonNull(data, "List item data is required.")
275                ));
276        }
277
278        /**
279         * Delete a list item.
280         *
281         * @param listId Unique ID of the list.
282         * @param itemId Unique ID of the item.
283         *
284         * @throws ProactiveConnectResponseException If the list or item does not exist or couldn't be deleted.
285         */
286        public void deleteListItem(UUID listId, UUID itemId) {
287                deleteListItem.execute(new ListItemRequestWrapper(
288                                validateUuid("List ID", listId), validateUuid("Item ID", itemId), null
289                ));
290        }
291
292        /**
293         * Download all items in a list in CSV format.
294         * Use {@link #downloadListItems(UUID, Path)} to save the CSV as a file.
295         *
296         * @param listId Unique ID of the list.
297         *
298         * @return The list items contents as a CSV-formatted String.
299         * @see #downloadListItems(UUID, Path)
300         *
301         * @throws ProactiveConnectResponseException If the list does not exist or couldn't be retrieved.
302         */
303        public String downloadListItems(UUID listId) {
304                return new String(downloadListItems.execute(validateUuid("List ID", listId)));
305        }
306
307        /**
308         * Download all items in a list in CSV format.
309         * Use {@link #downloadListItems(UUID)} to get the results as a String.
310         *
311         * @param listId Unique ID of the list.
312         * @param file Path of the file to write the downloaded results to.
313         *
314         * @throws ProactiveConnectResponseException If the list does not exist or couldn't be retrieved.
315         */
316        public void downloadListItems(UUID listId, Path file) {
317                try {
318                        Files.write(
319                                        Objects.requireNonNull(file, "CSV file is required."),
320                                        downloadListItems.execute(validateUuid("List ID", listId))
321                        );
322                }
323                catch (IOException ex) {
324                        throw new VonageUnexpectedException("Couldn't write list '"+listId+"' to file '"+file+"'", ex);
325                }
326        }
327
328        /**
329         * Import list items from a CSV file.
330         *
331         * @param listId Unique ID of the list.
332         * @param csvFile Path to the CSV file to upload.
333         *
334         * @return Result of the upload if successful.
335         *
336         * @throws ProactiveConnectResponseException If the request was unsuccessful.
337         * This could be for the following reasons:
338         * <ul>
339         *       <li><b>404</b>: List not found.</li>
340         *       <li><b>422</b>: Resource limit reached / exceeded.</li>
341         * </ul>
342         */
343        public UploadListItemsResponse uploadListItems(UUID listId, Path csvFile) {
344                try {
345                        byte[] data = Files.readAllBytes(csvFile);
346                        return uploadListItems.execute(new UploadListItemsRequestWrapper(
347                                        validateUuid("List ID", listId), data
348                        ));
349                }
350                catch (IOException ex) {
351                        throw new VonageClientException("Could not read from file.", ex);
352                }
353        }
354
355        /**
356         * Gets the first 1000 events in the application.
357         *
358         * @param listId Unique ID of the list to retrieve items from.
359         *
360         * @return The events in order of creation.
361         *
362         * @throws ProactiveConnectResponseException If the list does not exist or the items couldn't be retrieved.
363         */
364        public List<ListItem> listItems(UUID listId) {
365                return halRequest(listItems,
366                                validateUuid("List ID", listId).toString(),
367                                1, 1000, null
368                ).getItems();
369        }
370
371        /**
372         * Get all items on a particular page.
373         *
374         * @param listId Unique ID of the list to retrieve items from.
375         * @param page The page number of the HAL response to parse results.
376         * @param pageSize Number of results per page in the HAL response.
377         * @param order The order to sort results by (ascending or descending).
378         *
379         * @return The items page.
380         *
381         * @throws ProactiveConnectResponseException If the list does not exist or the items couldn't be retrieved.
382         */
383        public ListItemsResponse listItems(UUID listId, int page, int pageSize, SortOrder order) {
384                return halRequest(listItems, validateUuid("List ID", listId).toString(), page, pageSize, order);
385        }
386
387        /**
388         * Gets all events in the application matching the criteria.
389         *
390         * @param filter Optional attributes to narrow down the results.
391         *
392         * @return The list of events applicable to the request criteria, in order of creation.
393         *
394         * @throws ProactiveConnectResponseException If the events couldn't be retrieved.
395         */
396        public List<Event> listEvents(ListEventsFilter filter) {
397                return listEvents.execute(filter != null ? filter : ListEventsFilter.builder().build()).getEvents();
398        }
399}