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.account; 017 018import com.vonage.client.*; 019import com.vonage.client.auth.SignatureAuthMethod; 020import com.vonage.client.auth.ApiKeyHeaderAuthMethod; 021import com.vonage.client.common.HttpMethod; 022import java.util.List; 023import java.util.Objects; 024import java.util.function.Function; 025import java.util.function.Supplier; 026 027/** 028 * A client for talking to the Vonage Account API. The standard way to obtain an instance of this class is to use {@link 029 * VonageClient#getAccountClient()}. 030 */ 031public class AccountClient { 032 final Supplier<String> apiKeyGetter; 033 034 final RestEndpoint<Void, BalanceResponse> balance; 035 final RestEndpoint<PricingRequest, PricingResponse> pricing; 036 final RestEndpoint<ServiceType, FullPricingResponse> fullPricing; 037 final RestEndpoint<PrefixPricingRequest, PrefixPricingResponse> prefixPricing; 038 final RestEndpoint<TopUpRequest, Void> topUp; 039 final RestEndpoint<SettingsRequest, SettingsResponse> settings; 040 final RestEndpoint<String, ListSecretsResponse> listSecrets; 041 final RestEndpoint<SecretRequest, SecretResponse> getSecret; 042 final RestEndpoint<CreateSecretRequest, SecretResponse> createSecret; 043 final RestEndpoint<SecretRequest, Void> revokeSecret; 044 045 /** 046 * Constructor. 047 * 048 * @param wrapper (required) shared HTTP wrapper object used for making REST calls. 049 */ 050 @SuppressWarnings("unchecked") 051 public AccountClient(HttpWrapper wrapper) { 052 apiKeyGetter = () -> wrapper.getAuthCollection().getAuth(ApiKeyHeaderAuthMethod.class).getApiKey(); 053 054 class Endpoint<T, R> extends DynamicEndpoint<T, R> { 055 static final String SECRETS_PATH = "s/%s/secrets"; 056 057 Endpoint(Function<T, String> pathGetter, R... type) { 058 this(pathGetter, HttpMethod.GET, type); 059 } 060 Endpoint(Function<T, String> pathGetter, HttpMethod method, R... type) { 061 this(pathGetter, method, false, method == HttpMethod.POST, type); 062 } 063 Endpoint(Function<T, String> pathGetter, HttpMethod method, 064 boolean signatureAuth, boolean formEncoded, R... type 065 ) { 066 super(DynamicEndpoint.<T, R> builder(type) 067 .wrapper(wrapper).requestMethod(method) 068 .authMethod(ApiKeyHeaderAuthMethod.class, signatureAuth ? SignatureAuthMethod.class : null) 069 .responseExceptionType(AccountResponseException.class) 070 .urlFormEncodedContentType(formEncoded).pathGetter((de, req) -> { 071 HttpConfig config = de.getHttpWrapper().getHttpConfig(); 072 String base = signatureAuth ? config.getApiBaseUri() : config.getRestBaseUri(); 073 return base + "/account" + pathGetter.apply(req); 074 }) 075 ); 076 } 077 } 078 079 class SecretsEndpoint<T, R> extends Endpoint<T, R> { 080 SecretsEndpoint(Function<T, String> apiKeyGetter, HttpMethod method, R... type) { 081 super(req -> String.format(SECRETS_PATH, apiKeyGetter.apply(req)), method, true, false, type); 082 } 083 } 084 085 class SecretRequestEndpoint<R> extends Endpoint<SecretRequest, R> { 086 SecretRequestEndpoint(HttpMethod method, R... type) { 087 super(req -> String.format(SECRETS_PATH, req.apiKey) + "/" + req.secretId, 088 method, true, false, type 089 ); 090 } 091 } 092 093 balance = new Endpoint<>(req -> "/get-balance"); 094 pricing = new Endpoint<>(req -> "/get-pricing/outbound/" + req.serviceType); 095 fullPricing = new Endpoint<>(req -> "/get-full-pricing/outbound/" + req); 096 prefixPricing = new Endpoint<>(req -> "/get-prefix-pricing/outbound/" + req.serviceType); 097 topUp = new Endpoint<>(req -> "/top-up", HttpMethod.POST); 098 settings = new Endpoint<>(req -> "/settings", HttpMethod.POST); 099 listSecrets = new SecretsEndpoint<>(Function.identity(), HttpMethod.GET); 100 createSecret = new SecretsEndpoint<>(req -> req.apiKey, HttpMethod.POST); 101 getSecret = new SecretRequestEndpoint<>(HttpMethod.GET); 102 revokeSecret = new SecretRequestEndpoint<>(HttpMethod.DELETE); 103 } 104 105 /** 106 * Obtains the current account remaining balance. 107 * 108 * @return The account balance along with other metadata. 109 * 110 * @throws AccountResponseException If the balance could not be retrieved. 111 */ 112 public BalanceResponse getBalance() throws AccountResponseException { 113 return balance.execute(null); 114 } 115 116 /** 117 * Obtain pricing data on all supported countries for a specific service type. 118 * 119 * @param service The service type to retrieve pricing and network information for. 120 * 121 * @return The list of pricing information for all supported countries. 122 * 123 * @throws AccountResponseException If the pricing data could not be retrieved. 124 * 125 * @since v7.9.0 126 */ 127 public List<PricingResponse> listPriceAllCountries(ServiceType service) { 128 return fullPricing.execute(Objects.requireNonNull(service, "Service type is required.")).countries; 129 } 130 131 /** 132 * Retrieve the voice pricing for a specified country. 133 * 134 * @param country The two-character country code for which you would like to retrieve pricing. 135 * 136 * @return PricingResponse object which contains the results from the API. 137 * 138 * @throws AccountResponseException If there was an error making the request or retrieving the response. 139 */ 140 public PricingResponse getVoicePrice(String country) throws AccountResponseException { 141 return pricing.execute(new PricingRequest(country, ServiceType.VOICE)); 142 } 143 144 /** 145 * Retrieve the SMS pricing for a specified country. 146 * 147 * @param country The two-character country code for which you would like to retrieve pricing. 148 * 149 * @return PricingResponse object which contains the results from the API. 150 * 151 * @throws AccountResponseException If there was an error making the request or retrieving the response. 152 */ 153 public PricingResponse getSmsPrice(String country) throws AccountResponseException { 154 return pricing.execute(new PricingRequest(country, ServiceType.SMS)); 155 } 156 157 /** 158 * Retrieve the pricing for a specified prefix. 159 * 160 * @param type The type of service to retrieve pricing for. 161 * @param prefix The prefix to retrieve the pricing for. 162 * 163 * @return PrefixPricingResponse object which contains the results from the API. 164 * 165 * @throws AccountResponseException If there was an error making the request or retrieving the response. 166 */ 167 public PrefixPricingResponse getPrefixPrice(ServiceType type, String prefix) throws AccountResponseException { 168 return prefixPricing.execute(new PrefixPricingRequest(type, prefix)); 169 } 170 171 /** 172 * Top-up your account when you have enabled auto-reload in the dashboard. Amount added is based on your initial 173 * reload-enabled payment. 174 * 175 * @param transaction The ID associated with your original auto-reload transaction 176 * 177 * @throws AccountResponseException If there was an error making the request or retrieving the response. 178 */ 179 public void topUp(String transaction) throws AccountResponseException { 180 topUp.execute(new TopUpRequest(transaction)); 181 } 182 183 /** 184 * List the ID of each secret associated with this account's main API key. 185 * 186 * @return ListSecretsResponse object which contains the results from the API. 187 * 188 * @throws AccountResponseException If there was an error making the request or retrieving the response. 189 * 190 * @since 7.9.0 191 */ 192 public ListSecretsResponse listSecrets() throws AccountResponseException { 193 return listSecrets(apiKeyGetter.get()); 194 } 195 196 /** 197 * List the ID of each secret associated to the given API key. 198 * 199 * @param apiKey The API key to look up secrets for. 200 * 201 * @return ListSecretsResponse object which contains the results from the API. 202 * 203 * @throws AccountResponseException If there was an error making the request or retrieving the response. 204 */ 205 public ListSecretsResponse listSecrets(String apiKey) throws AccountResponseException { 206 if (apiKey == null || apiKey.trim().isEmpty()) { 207 throw new IllegalArgumentException("API key is required."); 208 } 209 return listSecrets.execute(apiKey); 210 } 211 212 /** 213 * Get information for a specific secret id associated with this account's main API key. 214 * 215 * @param secretId The id of the secret to get information on. 216 * 217 * @return SecretResponse object which contains the results from the API. 218 * 219 * @throws AccountResponseException If there was an error making the request or retrieving the response. 220 * 221 * @since 7.9.0 222 */ 223 public SecretResponse getSecret(String secretId) throws AccountResponseException { 224 return getSecret(apiKeyGetter.get(), secretId); 225 } 226 227 /** 228 * Get information for a specific secret id associated to a given API key. 229 * 230 * @param apiKey The API key that the secret is associated to. 231 * @param secretId The id of the secret to get information on. 232 * 233 * @return SecretResponse object which contains the results from the API. 234 * 235 * @throws AccountResponseException If there was an error making the request or retrieving the response. 236 */ 237 public SecretResponse getSecret(String apiKey, String secretId) throws AccountResponseException { 238 return getSecret.execute(new SecretRequest(apiKey, secretId)); 239 } 240 241 /** 242 * Create a secret to be used with this account's main API key. 243 * 244 * @param secret The contents of the secret. 245 * 246 * @return SecretResponse object which contains the created secret from the API. 247 * 248 * @throws AccountResponseException If there was an error making the request or retrieving the response. 249 * 250 * @since 7.9.0 251 */ 252 public SecretResponse createSecret(String secret) throws AccountResponseException { 253 return createSecret(apiKeyGetter.get(), secret); 254 } 255 256 /** 257 * Create a secret to be used with a specific API key. 258 * 259 * @param apiKey The API key that the secret is to be used with. 260 * @param secret The contents of the secret. 261 * 262 * @return SecretResponse object which contains the created secret from the API. 263 * 264 * @throws AccountResponseException If there was an error making the request or retrieving the response. 265 */ 266 public SecretResponse createSecret(String apiKey, String secret) throws AccountResponseException { 267 return createSecret.execute(new CreateSecretRequest(apiKey, secret)); 268 } 269 270 /** 271 * Revoke a secret associated with this account's main API key. 272 * 273 * @param secretId The id of the secret to revoke. 274 * 275 * @throws AccountResponseException If there was an error making the request or retrieving the response. 276 * 277 * @since 7.9.0 278 */ 279 public void revokeSecret(String secretId) throws AccountResponseException { 280 revokeSecret(apiKeyGetter.get(), secretId); 281 } 282 283 /** 284 * Revoke a secret associated with a specific API key. 285 * 286 * @param apiKey The API key that the secret is associated to. 287 * @param secretId The id of the secret to revoke. 288 * 289 * @throws AccountResponseException If there was an error making the request or retrieving the response. 290 */ 291 public void revokeSecret(String apiKey, String secretId) throws AccountResponseException { 292 revokeSecret.execute(new SecretRequest(apiKey, secretId)); 293 } 294 295 /** 296 * Updates the account-level incoming SMS URL, as used by the SMS API. 297 * 298 * @param url The URL where Vonage will send a webhook when an incoming SMS is received when a 299 * number-specific URL is not configured. Set to an empty string to unset the value. 300 * 301 * @return A {@link SettingsResponse} containing the newly-updated account settings. 302 * 303 * @throws AccountResponseException If there was an error making the request or retrieving the response. 304 */ 305 public SettingsResponse updateSmsIncomingUrl(String url) throws AccountResponseException { 306 return updateSettings(SettingsRequest.withIncomingSmsUrl(url)); 307 } 308 309 /** 310 * Updates the account-level delivery receipt URL (mainly used by the SMS API). 311 * 312 * @param url The URL where Vonage will send a webhook when an incoming SMS is received when a 313 * number-specific URL is not configured. Set to an empty string to unset the value. 314 * 315 * @return A {@link SettingsResponse} containing the newly-updated account settings. 316 * 317 * @throws AccountResponseException If there was an error making the request or retrieving the response. 318 */ 319 public SettingsResponse updateDeliveryReceiptUrl(String url) throws AccountResponseException { 320 return updateSettings(SettingsRequest.withDeliveryReceiptUrl(url)); 321 } 322 323 /** 324 * Updates the account-level settings. 325 * 326 * @param request The {@link SettingsRequest} containing the fields to update. 327 * 328 * @return A {@link SettingsResponse} containing the newly-updated account settings. 329 * 330 * @throws AccountResponseException If there was an error making the request or retrieving the response. 331 * 332 * @deprecated Use {@link #updateSmsIncomingUrl(String)} or {@link #updateDeliveryReceiptUrl(String)} instead. 333 */ 334 @Deprecated 335 public SettingsResponse updateSettings(SettingsRequest request) throws AccountResponseException { 336 return settings.execute(Objects.requireNonNull(request, "Settings request cannot be null.")); 337 } 338}