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}