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.users;
017
018import com.vonage.client.DynamicEndpoint;
019import com.vonage.client.HttpWrapper;
020import com.vonage.client.RestEndpoint;
021import com.vonage.client.VonageClient;
022import com.vonage.client.auth.JWTAuthMethod;
023import com.vonage.client.common.HttpMethod;
024import java.util.List;
025import java.util.Objects;
026import java.util.UUID;
027import java.util.function.Function;
028import java.util.stream.Collectors;
029
030/**
031 * A client for talking to the Vonage Users API. The standard way to obtain an instance of
032 * this class is to use {@link VonageClient#getUsersClient()}.
033 */
034public class UsersClient {
035    final RestEndpoint<ListUsersRequest, ListUsersResponse> listUsers;
036    final RestEndpoint<User, User> createUser;
037    final RestEndpoint<String, User> getUser;
038    final RestEndpoint<User, User> updateUser;
039    final RestEndpoint<String, Void> deleteUser;
040
041    public UsersClient(HttpWrapper wrapper) {
042        @SuppressWarnings("unchecked")
043        final class Endpoint<T, R> extends DynamicEndpoint<T, R> {
044            Endpoint(Function<T, String> pathGetter, HttpMethod method, R... type) {
045                super(DynamicEndpoint.<T, R> builder(type)
046                        .responseExceptionType(UsersResponseException.class)
047                        .wrapper(wrapper).requestMethod(method).authMethod(JWTAuthMethod.class)
048                        .pathGetter((de, req) -> {
049                            String base = de.getHttpWrapper().getHttpConfig().getVersionedApiBaseUri("v1");
050                            String path = base + "/users";
051                            if (pathGetter != null) {
052                                path += "/" + pathGetter.apply(req);
053                            }
054                            return path;
055                        })
056                );
057            }
058        }
059
060        listUsers = new Endpoint<>(null, HttpMethod.GET);
061        createUser = new Endpoint<>(null, HttpMethod.POST);
062        getUser = new Endpoint<>(Function.identity(), HttpMethod.GET);
063        updateUser = new Endpoint<>(User::getId, HttpMethod.PATCH);
064        deleteUser = new Endpoint<>(Function.identity(), HttpMethod.DELETE);
065    }
066
067    private <U extends BaseUser> U validateUser(U request) {
068        return Objects.requireNonNull(request, "User is required.");
069    }
070
071    private String validateUserId(String id) {
072        Objects.requireNonNull(id, "User ID is required.");
073        String prefix = "USR-";
074        if (!id.startsWith(prefix)) {
075            throw new IllegalArgumentException("Invalid user ID.");
076        }
077        return prefix + UUID.fromString(id.substring(prefix.length()));
078    }
079
080    /**
081     * Create a new user.
082     *
083     * @param user The properties for the user to be created with.
084     *
085     * @return The user which has been created.
086     *
087     * @throws UsersResponseException If there was an error processing the request.
088     */
089    public User createUser(User user) throws UsersResponseException {
090        return createUser.execute(validateUser(user));
091    }
092
093    /**
094     * Update an existing user.
095     *
096     * @param userId Unique ID of the user to update.
097     * @param user The properties for the user to be updated with.
098     *
099     * @return The user which has been updated.
100     *
101     * @throws UsersResponseException If there was an error processing the request.
102     */
103    public User updateUser(String userId, User user) throws UsersResponseException {
104        validateUser(user).id = validateUserId(userId);
105        return updateUser.execute(user);
106    }
107
108    /**
109     * Retrieve a user.
110     *
111     * @param userId Unique ID of the user to retrieve.
112     *
113     * @return The corresponding user.
114     *
115     * @throws UsersResponseException If there was an error processing the request.
116     */
117    public User getUser(String userId) throws UsersResponseException {
118        return getUser.execute(validateUserId(userId));
119    }
120
121    /**
122     * Delete a user.
123     *
124     * @param userId Unique ID of the user to delete.
125     *
126     * @throws UsersResponseException If there was an error processing the request.
127     */
128    public void deleteUser(String userId) throws UsersResponseException {
129        deleteUser.execute(validateUserId(userId));
130    }
131
132    /**
133     * Lists users in the application based on the filter criteria.
134     *
135     * @param request Optional parameters to customise the search results.
136     *
137     * @return The HAL response containing the users.
138     *
139     * @throws UsersResponseException If there was an error processing the request.
140     */
141    public ListUsersResponse listUsers(ListUsersRequest request) throws UsersResponseException {
142        return listUsers.execute(request != null ? request : ListUsersRequest.builder().build());
143    }
144
145    /**
146     * Lists the first 100 users in the application, from newest to oldest. <br>
147     * <b>NOTE: The users returned from this method will only contain the name and ID, not the full record.</b>
148     *
149     * @return The list of available users in creation order.
150     *
151     * @throws UsersResponseException If there was an error processing the request.
152     *
153     * @see #listUsers(ListUsersRequest)
154     */
155    public List<BaseUser> listUsers() throws UsersResponseException {
156        return listUsers(ListUsersRequest.builder().pageSize(100).build()).getUsers();
157    }
158
159    /**
160     * Convenience method that uses the metadata from the base user object to obtain all known fields about the user.
161     *
162     * @param minimalUser The user metadata.
163     *
164     * @return A new {@linkplain User} object with all known fields populated.
165     *
166     * @throws UsersResponseException If there was an error processing the request.
167     */
168    public User getUserDetails(BaseUser minimalUser) {
169        return getUser(validateUser(minimalUser).getId());
170    }
171
172    /**
173     * Convenience method that uses the metadata from the base users list to obtain all known fields about the users.
174     * You may use this method to convert the minimal data returned from {@link #listUsers()} or
175     * {@link ListUsersResponse#getUsers()} into fully populated records.
176     *
177     * @param minimalUsers The list of users' metadata.
178     *
179     * @return A list of users with all known fields populated in the same encounter order.
180     *
181     * @throws UsersResponseException If there was an error processing the request.
182     */
183    public List<User> getUserDetails(List<BaseUser> minimalUsers) {
184        return Objects.requireNonNull(minimalUsers, "Users list is required")
185                .stream().map(this::getUserDetails).collect(Collectors.toList());
186    }
187}