001/*
002 *   Copyright 2020 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;
017
018
019import com.vonage.client.account.AccountClient;
020import com.vonage.client.application.ApplicationClient;
021import com.vonage.client.auth.*;
022import com.vonage.client.conversion.ConversionClient;
023import com.vonage.client.insight.InsightClient;
024import com.vonage.client.numbers.NumbersClient;
025import com.vonage.client.redact.RedactClient;
026import com.vonage.client.sms.SmsClient;
027import com.vonage.client.sns.SnsClient;
028import com.vonage.client.verify.VerifyClient;
029import com.vonage.client.voice.VoiceClient;
030import org.apache.http.client.HttpClient;
031
032import java.io.IOException;
033import java.nio.file.Files;
034import java.nio.file.Path;
035import java.nio.file.Paths;
036
037/**
038 * Top-level Vonage API client object.
039 * <p>
040 * Construct an instance of this object with one or more {@link AuthMethod}s (providing all the authentication methods
041 * for the APIs you wish to use), and then call {@link #getVoiceClient()} to obtain a client for the Vonage Voice API.
042 * <p>
043 * Currently this object only constructs and provides access to {@link VoiceClient}. In the future it will manage
044 * clients for all of the Vonage APIs.
045 */
046public class VonageClient {
047    private AccountClient account;
048    private ApplicationClient application;
049    private InsightClient insight;
050    private NumbersClient numbers;
051    private SmsClient sms;
052    private VoiceClient voice;
053    private VerifyClient verify;
054    private SnsClient sns;
055    private ConversionClient conversion;
056    private RedactClient redact;
057    private HttpWrapper httpWrapper;
058
059    private VonageClient(Builder builder) {
060        this.httpWrapper = new HttpWrapper(builder.httpConfig, builder.authCollection);
061        this.httpWrapper.setHttpClient(builder.httpClient);
062
063        this.account = new AccountClient(this.httpWrapper);
064        this.application = new ApplicationClient(this.httpWrapper);
065        this.insight = new InsightClient(this.httpWrapper);
066        this.numbers = new NumbersClient(this.httpWrapper);
067        this.verify = new VerifyClient(this.httpWrapper);
068        this.voice = new VoiceClient(this.httpWrapper);
069        this.sms = new SmsClient(this.httpWrapper);
070        this.sns = new SnsClient(this.httpWrapper);
071        this.conversion = new ConversionClient(this.httpWrapper);
072        this.redact = new RedactClient(this.httpWrapper);
073    }
074
075    public AccountClient getAccountClient() {
076        return this.account;
077    }
078
079    public ApplicationClient getApplicationClient() {
080        return this.application;
081    }
082
083    public InsightClient getInsightClient() {
084        return this.insight;
085    }
086
087    public NumbersClient getNumbersClient() {
088        return this.numbers;
089    }
090
091    public SmsClient getSmsClient() {
092        return this.sms;
093    }
094
095    public SnsClient getSnsClient() {
096        return this.sns;
097    }
098
099    public VerifyClient getVerifyClient() {
100        return this.verify;
101    }
102
103    public VoiceClient getVoiceClient() {
104        return this.voice;
105    }
106
107    public ConversionClient getConversionClient() {
108        return this.conversion;
109    }
110
111    public RedactClient getRedactClient() {
112        return this.redact;
113    }
114
115    /**
116     * Generate a JWT for the application the client has been configured with.
117     *
118     * @return A String containing the token data.
119     *
120     * @throws VonageUnacceptableAuthException if no {@link JWTAuthMethod} is available
121     */
122    public String generateJwt() throws VonageUnacceptableAuthException {
123        JWTAuthMethod authMethod = this.httpWrapper.getAuthCollection().getAuth(JWTAuthMethod.class);
124        return authMethod.generateToken();
125    }
126
127    /**
128     * @return The {@link HttpWrapper}
129     */
130    HttpWrapper getHttpWrapper() {
131        return httpWrapper;
132    }
133
134    public static Builder builder() {
135        return new Builder();
136    }
137
138    public static class Builder {
139        private AuthCollection authCollection;
140        private HttpConfig httpConfig = HttpConfig.defaultConfig();
141        private HttpClient httpClient;
142        private String applicationId;
143        private String apiKey;
144        private String apiSecret;
145        private String signatureSecret;
146        private byte[] privateKeyContents;
147
148        /**
149         * @param httpConfig Configuration options for the {@link HttpWrapper}
150         *
151         * @return The {@link Builder} to keep building.
152         */
153        public Builder httpConfig(HttpConfig httpConfig) {
154            this.httpConfig = httpConfig;
155            return this;
156        }
157
158        /**
159         * @param httpClient Custom implementation of {@link HttpClient}.
160         *
161         * @return The {@link Builder} to keep building.
162         */
163        public Builder httpClient(HttpClient httpClient) {
164            this.httpClient = httpClient;
165            return this;
166        }
167
168        /**
169         * When setting an applicationId, it is also expected that the {@link #privateKeyContents} will also be set.
170         *
171         * @param applicationId Used to identify each application.
172         *
173         * @return The {@link Builder} to keep building.
174         */
175        public Builder applicationId(String applicationId) {
176            this.applicationId = applicationId;
177            return this;
178        }
179
180        /**
181         * When setting an apiKey, it is also expected that {@link #apiSecret(String)} and/or {@link
182         * #signatureSecret(String)} will also be set.
183         *
184         * @param apiKey The API Key found in the dashboard for your account.
185         *
186         * @return The {@link Builder} to keep building.
187         */
188        public Builder apiKey(String apiKey) {
189            this.apiKey = apiKey;
190            return this;
191        }
192
193        /**
194         * When setting an apiSecret, it is also expected that {@link #apiKey(String)} will also be set.
195         *
196         * @param apiSecret The API Secret found in the dashboard for your account.
197         *
198         * @return The {@link Builder} to keep building.
199         */
200        public Builder apiSecret(String apiSecret) {
201            this.apiSecret = apiSecret;
202            return this;
203        }
204
205        /**
206         * When setting a signatureSecret, it is also expected that {@link #apiKey(String)} will also be set.
207         *
208         * @param signatureSecret The Signature Secret found in the dashboard for your account.
209         *
210         * @return The {@link Builder} to keep building.
211         */
212        public Builder signatureSecret(String signatureSecret) {
213            this.signatureSecret = signatureSecret;
214            return this;
215        }
216
217        /**
218         * When setting the contents of your private key, it is also expected that {@link #applicationId(String)} will
219         * also be set.
220         *
221         * @param privateKeyContents The contents of your private key used for JWT generation.
222         *
223         * @return The {@link Builder} to keep building.
224         */
225        public Builder privateKeyContents(byte[] privateKeyContents) {
226            this.privateKeyContents = privateKeyContents;
227            return this;
228        }
229
230        /**
231         * When setting the contents of your private key, it is also expected that {@link #applicationId(String)} will
232         * also be set.
233         *
234         * @param privateKeyContents The contents of your private key used for JWT generation.
235         *
236         * @return The {@link Builder} to keep building.
237         */
238        public Builder privateKeyContents(String privateKeyContents) {
239            return privateKeyContents(privateKeyContents.getBytes());
240        }
241
242        /**
243         * When setting the path of your private key, it is also expected that {@link #applicationId(String)} will also
244         * be set.
245         *
246         * @param privateKeyPath The path to your private key used for JWT generation.
247         *
248         * @return The {@link Builder} to keep building.
249         *
250         * @throws VonageUnableToReadPrivateKeyException if the private key could not be read from the file system.
251         */
252        public Builder privateKeyPath(Path privateKeyPath) throws VonageUnableToReadPrivateKeyException {
253            try {
254                return privateKeyContents(Files.readAllBytes(privateKeyPath));
255            } catch (IOException e) {
256                throw new VonageUnableToReadPrivateKeyException("Unable to read private key at " + privateKeyPath, e);
257            }
258        }
259
260        /**
261         * When setting the path of your private key, it is also expected that {@link #applicationId(String)} will also
262         * be set.
263         *
264         * @param privateKeyPath The path to your private key used for JWT generation.
265         *
266         * @return The {@link Builder} to keep building.
267         *
268         * @throws VonageUnableToReadPrivateKeyException if the private key could not be read from the file system.
269         */
270        public Builder privateKeyPath(String privateKeyPath) throws VonageUnableToReadPrivateKeyException {
271            return privateKeyPath(Paths.get(privateKeyPath));
272        }
273
274        /**
275         * @return a new {@link VonageClient} from the stored builder options.
276         *
277         * @throws VonageClientCreationException if credentials aren't provided in a valid pairing or there were issues
278         *                                      generating an {@link JWTAuthMethod} with the provided credentials.
279         */
280        public VonageClient build() {
281            this.authCollection = generateAuthCollection(this.applicationId,
282                    this.apiKey,
283                    this.apiSecret,
284                    this.signatureSecret,
285                    this.privateKeyContents
286            );
287            return new VonageClient(this);
288        }
289
290        private AuthCollection generateAuthCollection(String applicationId, String key, String secret, String signature, byte[] privateKeyContents) {
291            AuthCollection authMethods = new AuthCollection();
292
293            try {
294                validateAuthParameters(applicationId, key, secret, signature, privateKeyContents);
295            } catch (IllegalStateException e) {
296                throw new VonageClientCreationException("Failed to generate authentication methods.", e);
297            }
298
299            if (key != null && secret != null) {
300                authMethods.add(new TokenAuthMethod(key, secret));
301            }
302
303            if (key != null && signature != null) {
304                authMethods.add(new SignatureAuthMethod(key, signature));
305            }
306
307            if (applicationId != null && privateKeyContents != null) {
308                authMethods.add(new JWTAuthMethod(applicationId, privateKeyContents));
309            }
310
311            return authMethods;
312        }
313
314        private void validateAuthParameters(String applicationId, String key, String secret, String signature, byte[] privateKeyContents) {
315            if (key != null && secret == null && signature == null) {
316                throw new IllegalStateException(
317                        "You must provide an API secret or signature secret in addition to your API key.");
318            }
319
320            if (secret != null && key == null) {
321                throw new IllegalStateException("You must provide an API key in addition to your API secret.");
322            }
323
324            if (signature != null && key == null) {
325                throw new IllegalStateException("You must provide an API key in addition to your signature secret.");
326            }
327
328            if (applicationId == null && privateKeyContents != null) {
329                throw new IllegalStateException("You must provide an application ID in addition to your private key.");
330            }
331
332            if (applicationId != null && privateKeyContents == null) {
333                throw new IllegalStateException("You must provide a private key in addition to your application id.");
334            }
335        }
336    }
337}