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.verify2; 017 018import com.vonage.client.DynamicEndpoint; 019import com.vonage.client.HttpWrapper; 020import com.vonage.client.RestEndpoint; 021import com.vonage.client.auth.JWTAuthMethod; 022import com.vonage.client.auth.ApiKeyHeaderAuthMethod; 023import com.vonage.client.common.HttpMethod; 024import java.util.List; 025import java.util.Objects; 026import java.util.UUID; 027import java.util.function.Function; 028 029public class Verify2Client { 030 final boolean hasJwtAuthMethod; 031 final RestEndpoint<VerificationRequest, VerificationResponse> verifyUser; 032 final RestEndpoint<VerifyCodeRequestWrapper, VerifyCodeResponse> verifyRequest; 033 final RestEndpoint<UUID, Void> cancel, nextWorkflow, deleteTemplate; 034 final RestEndpoint<ListTemplatesRequest, ListTemplatesResponse> listTemplates; 035 final RestEndpoint<UUID, Template> getTemplate; 036 final RestEndpoint<Template, Template> createTemplate, updateTemplate; 037 final RestEndpoint<ListTemplatesRequest, ListTemplateFragmentsResponse> listFragments; 038 final RestEndpoint<TemplateFragmentRequestWrapper, TemplateFragment> getFragment; 039 final RestEndpoint<TemplateFragmentRequestWrapper, Void> deleteFragment; 040 final RestEndpoint<TemplateFragment, TemplateFragment> createFragment, updateFragment; 041 042 /** 043 * Create a new Verify2Client. 044 * 045 * @param wrapper Http Wrapper used to create verification requests. 046 */ 047 public Verify2Client(HttpWrapper wrapper) { 048 hasJwtAuthMethod = wrapper.getAuthCollection().hasAuthMethod(JWTAuthMethod.class); 049 050 @SuppressWarnings("unchecked") 051 final class Endpoint<T, R> extends DynamicEndpoint<T, R> { 052 Endpoint(Function<T, String> pathGetter, HttpMethod method, R... type) { 053 super(DynamicEndpoint.<T, R> builder(type) 054 .responseExceptionType(VerifyResponseException.class) 055 .wrapper(wrapper).requestMethod(method) 056 .authMethod(JWTAuthMethod.class, ApiKeyHeaderAuthMethod.class) 057 .pathGetter((de, req) -> { 058 String base = de.getHttpWrapper().getHttpConfig().getApiBaseUri() + "/v2/verify"; 059 return pathGetter != null ? base + "/" + pathGetter.apply(req) : base; 060 }) 061 ); 062 } 063 } 064 065 verifyUser = new Endpoint<>(null, HttpMethod.POST); 066 verifyRequest = new Endpoint<>(req -> req.requestId, HttpMethod.POST); 067 cancel = new Endpoint<>(UUID::toString, HttpMethod.DELETE); 068 nextWorkflow = new Endpoint<>(id -> id + "/next-workflow", HttpMethod.POST); 069 070 final String templatesBase = "templates"; 071 listTemplates = new Endpoint<>(__ -> templatesBase, HttpMethod.GET); 072 getTemplate = new Endpoint<>(id -> templatesBase+'/'+id, HttpMethod.GET); 073 createTemplate = new Endpoint<>(__ -> templatesBase, HttpMethod.POST); 074 updateTemplate = new Endpoint<>(req -> templatesBase+'/'+req.id, HttpMethod.PATCH); 075 deleteTemplate = new Endpoint<>(id -> templatesBase+'/'+id, HttpMethod.DELETE); 076 077 final String fragmentsBase = "/template_fragments"; 078 listFragments = new Endpoint<>(req -> templatesBase+'/'+req.templateId + fragmentsBase, HttpMethod.GET); 079 getFragment = new Endpoint<>(req -> 080 templatesBase+'/'+req.templateId + fragmentsBase+'/'+req.fragmentId, HttpMethod.GET 081 ); 082 createFragment = new Endpoint<>(req -> templatesBase+'/'+req.templateId + fragmentsBase, HttpMethod.POST); 083 updateFragment = new Endpoint<>(req -> 084 templatesBase+'/'+req.getTemplateId() + fragmentsBase+'/'+req.fragmentId, HttpMethod.PATCH 085 ); 086 deleteFragment = new Endpoint<>(req -> 087 templatesBase+'/'+req.templateId + fragmentsBase+'/'+req.fragmentId, HttpMethod.DELETE 088 ); 089 } 090 091 private UUID validateId(String name, UUID id) { 092 return Objects.requireNonNull(id, name + " ID is required."); 093 } 094 095 private UUID validateRequestId(UUID requestId) { 096 return validateId("Request", requestId); 097 } 098 099 private UUID validateTemplateId(UUID templateId) { 100 return validateId("Template", templateId); 101 } 102 103 private UUID validateFragmentId(UUID fragmentId) { 104 return validateId("Fragment", fragmentId); 105 } 106 107 /** 108 * Request a verification be sent to a user. This is the first step in the verification process. 109 * 110 * @param request Properties of the verification request. You must specify the brand name and at least one 111 * contact method (workflow). For example, to verify using Whatsapp and fall back to a voice call as backup 112 * to the same number with a 6-digit code and a 3-minute wait between attempts: 113 * <pre> 114 * {@code VerificationRequest.builder() 115 * .brand("My Company") 116 * .addWorkflow(new WhatsappWorkflow("447000000001")) 117 * .addWorkflow(new VoiceWorkflow("447000000001")) 118 * .codeLength(6) 119 * .channelTimeout(180) 120 * .build()}. 121 * </pre> 122 * 123 * @return The server's response, if successful. 124 * 125 * @throws VerifyResponseException If the request was unsuccessful. This could be for the following reasons: 126 * <ul> 127 * <li><b>409</b>: Concurrent verifications to the same number are not allowed.</li> 128 * <li><b>422</b>: The value of one or more parameters is invalid.</li> 129 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 130 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 131 * </ul> 132 */ 133 public VerificationResponse sendVerification(VerificationRequest request) { 134 if (request.isCodeless() && !hasJwtAuthMethod) { 135 throw new IllegalStateException( 136 "Codeless verification requires an application ID to be set in order to use webhooks." 137 ); 138 } 139 return verifyUser.execute(Objects.requireNonNull(request)); 140 } 141 142 /** 143 * Check a supplied code against an existing verification request. If the code is valid, 144 * this method will return normally. Otherwise, a {@link VerifyResponseException} will be thrown. 145 * 146 * @param requestId ID of the verify request, obtained from {@link VerificationResponse#getRequestId()}. 147 * @param code The code supplied by the user. 148 * 149 * @return Details of the verification request (if the code matched). 150 * 151 * @throws VerifyResponseException If the code could not be verified. This could be for the following reasons: 152 * <ul> 153 * <li><b>400</b>: The provided code does not match the expected value.</li> 154 * <li><b>401</b>: Invalid credentials.</li> 155 * <li><b>402</b>: Low balance.</li> 156 * <li><b>404</b>: Request ID was not found or it has been verified already.</li> 157 * <li><b>409</b>: The current workflow step does not support a code.</li> 158 * <li><b>410</b>: An incorrect code has been provided too many times.</li> 159 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 160 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 161 * </ul> 162 */ 163 public VerifyCodeResponse checkVerificationCode(UUID requestId, String code) { 164 return verifyRequest.execute(new VerifyCodeRequestWrapper( 165 validateRequestId(requestId).toString(), 166 Objects.requireNonNull(code, "Code is required.") 167 )); 168 } 169 170 /** 171 * Attempts to abort an active verification workflow. 172 * If successful (HTTP status 204), this method will return normally. 173 * Otherwise, a {@link VerifyResponseException} exception will be thrown, indicating a 404 response. 174 * 175 * @param requestId ID of the verify request, obtained from {@link VerificationResponse#getRequestId()}. 176 * 177 * @throws VerifyResponseException If the request could not be cancelled. This could be for the following reasons: 178 * <ul> 179 * <li><b>401</b>: Invalid credentials.</li> 180 * <li><b>402</b>: Low balance.</li> 181 * <li><b>404</b>: Request ID was not found or it has been verified / cancelled already.</li> 182 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 183 * </ul> 184 */ 185 public void cancelVerification(UUID requestId) { 186 cancel.execute(validateRequestId(requestId)); 187 } 188 189 /** 190 * Move the request onto the next workflow, if available. If successful, this method will return normally. 191 * Otherwise, a {@link VerifyResponseException} will be thrown. 192 * 193 * @param requestId ID of the verify request, obtained from {@link VerificationResponse#getRequestId()}. 194 * 195 * @throws VerifyResponseException If the workflow could not be advanced. This could be for the following reasons: 196 * <ul> 197 * <li><b>401</b>: Invalid credentials.</li> 198 * <li><b>404</b>: Request ID was not found or it has been verified already.</li> 199 * <li><b>409</b>: There are no more events left to trigger.</li> 200 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 201 * </ul> 202 * 203 * @since 8.5.0 204 */ 205 public void nextWorkflow(UUID requestId) { 206 nextWorkflow.execute(validateRequestId(requestId)); 207 } 208 209 /** 210 * Create a new custom template. 211 * 212 * @param name Reference name for the template. Must not contain spaces or special characters other than _ and -. 213 * 214 * @return The created template metadata. 215 * 216 * @throws VerifyResponseException If the template could not be created. This could be for the following reasons: 217 * <ul> 218 * <li><b>401</b>: Invalid credentials.</li> 219 * <li><b>402</b>: Low balance.</li> 220 * <li><b>403</b>: Template management is not enabled for your account.</li> 221 * <li><b>409</b>: A template with the same name already exists, or you have more than 9 templates.</li> 222 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 223 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 224 * </ul> 225 * 226 * @since 8.13.0 227 */ 228 public Template createTemplate(String name) { 229 return createTemplate.execute(new Template(Objects.requireNonNull(name, "Name is required."), null, null)); 230 } 231 232 /** 233 * List all custom templates associated with the account. 234 * 235 * @return The list of templates. 236 * 237 * @throws VerifyResponseException If the templates could not be retrieved. This could be for the following reasons: 238 * <ul> 239 * <li><b>401</b>: Invalid credentials.</li> 240 * <li><b>402</b>: Low balance.</li> 241 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 242 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 243 * </ul> 244 * 245 * @since 8.13.0 246 */ 247 public List<Template> listTemplates() { 248 return listTemplates(1, 100).getTemplates(); 249 } 250 251 // Not useful since there can only be 10 templates at a time. 252 ListTemplatesResponse listTemplates(Integer page, Integer pageSize) { 253 return listTemplates.execute(new ListTemplatesRequest(page, pageSize, null)); 254 } 255 256 /** 257 * Retrieve a specific template. 258 * 259 * @param templateId ID of the template to retrieve. 260 * 261 * @return The template metadata. 262 * 263 * @throws VerifyResponseException If the template could not be retrieved. This could be for the following reasons: 264 * <ul> 265 * <li><b>401</b>: Invalid credentials.</li> 266 * <li><b>402</b>: Low balance.</li> 267 * <li><b>404</b>: Template ID was not found.</li> 268 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 269 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 270 * </ul> 271 * 272 * @since 8.13.0 273 */ 274 public Template getTemplate(UUID templateId) { 275 return getTemplate.execute(validateTemplateId(templateId)); 276 } 277 278 /** 279 * Update an existing template. 280 * 281 * @param templateId ID of the template to update. 282 * @param name New reference name for the template. Must not contain spaces or special characters. 283 * @param isDefault Whether this template should be the default for the account. 284 * 285 * @return The updated template metadata. 286 * 287 * @throws VerifyResponseException If the template could not be updated. This could be for the following reasons: 288 * <ul> 289 * <li><b>401</b>: Invalid credentials.</li> 290 * <li><b>402</b>: Low balance.</li> 291 * <li><b>403</b>: Template management is not enabled for your account.</li> 292 * <li><b>404</b>: Template ID was not found.</li> 293 * <li><b>409</b>: A template with the same name already exists, or you have more than 9 templates.</li> 294 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 295 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 296 * </ul> 297 * 298 * @since 8.13.0 299 */ 300 public Template updateTemplate(UUID templateId, String name, Boolean isDefault) { 301 return updateTemplate.execute(new Template(name, isDefault, validateTemplateId(templateId))); 302 } 303 304 /** 305 * Delete a template. 306 * 307 * @param templateId ID of the template to delete. 308 * 309 * @throws VerifyResponseException If the template could not be deleted. This could be for the following reasons: 310 * <ul> 311 * <li><b>401</b>: Invalid credentials.</li> 312 * <li><b>402</b>: Low balance.</li> 313 * <li><b>403</b>: Template management is not enabled for your account.</li> 314 * <li><b>404</b>: Template not found.</li> 315 * <li><b>409</b>: Template contains undeleted fragments or is the default.</li> 316 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 317 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 318 * </ul> 319 * 320 * @since 8.13.0 321 */ 322 public void deleteTemplate(UUID templateId) { 323 deleteTemplate.execute(validateTemplateId(templateId)); 324 } 325 326 /** 327 * Create a new template fragment. 328 * 329 * @param templateId ID of the template to which the fragment belongs. 330 * @param fragment The fragment to create. 331 * 332 * @return The created fragment metadata. 333 * 334 * @throws VerifyResponseException If the fragment could not be created. This could be for the following reasons: 335 * <ul> 336 * <li><b>401</b>: Invalid credentials.</li> 337 * <li><b>402</b>: Low balance.</li> 338 * <li><b>403</b>: Template management is not enabled for your account.</li> 339 * <li><b>404</b>: Template ID was not found.</li> 340 * <li><b>409</b>: Fragment for this channel and locale already exists.</li> 341 * <li><b>422</b>: Invalid parameters (e.g. unsupported locale or invalid text variables).</li> 342 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 343 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 344 * </ul> 345 * 346 * @since 8.13.0 347 */ 348 public TemplateFragment createTemplateFragment(UUID templateId, TemplateFragment fragment) { 349 Objects.requireNonNull(fragment, "Template fragment is required.").templateId = validateTemplateId(templateId); 350 return createFragment.execute(fragment); 351 } 352 353 /** 354 * List all fragments associated with a template. 355 * 356 * @param templateId ID of the template to list fragments for. 357 * 358 * @return The list of fragments. 359 * 360 * @throws VerifyResponseException If the fragments could not be retrieved. This could be for the following reasons: 361 * <ul> 362 * <li><b>401</b>: Invalid credentials.</li> 363 * <li><b>402</b>: Low balance.</li> 364 * <li><b>404</b>: Template ID was not found.</li> 365 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 366 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 367 * </ul> 368 * 369 * @since 8.13.0 370 */ 371 public List<TemplateFragment> listTemplateFragments(UUID templateId) { 372 return listTemplateFragments(templateId, 1, 1000).getTemplateFragments(); 373 } 374 375 // Not useful in the general case to expose. 376 ListTemplateFragmentsResponse listTemplateFragments(UUID templateId, Integer page, Integer pageSize) { 377 return listFragments.execute(new ListTemplatesRequest(page, pageSize, validateTemplateId(templateId))); 378 } 379 380 /** 381 * Retrieve a specific fragment. 382 * 383 * @param templateId ID of the template to which the fragment belongs. 384 * @param fragmentId ID of the fragment to retrieve. 385 * 386 * @return The fragment metadata. 387 * 388 * @throws VerifyResponseException If the fragment could not be retrieved. This could be for the following reasons: 389 * <ul> 390 * <li><b>401</b>: Invalid credentials.</li> 391 * <li><b>402</b>: Low balance.</li> 392 * <li><b>404</b>: Fragment not found for the provided IDs.</li> 393 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 394 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 395 * </ul> 396 * 397 * @since 8.13.0 398 */ 399 public TemplateFragment getTemplateFragment(UUID templateId, UUID fragmentId) { 400 return getFragment.execute(new TemplateFragmentRequestWrapper( 401 validateTemplateId(templateId), validateFragmentId(fragmentId) 402 )); 403 } 404 405 /** 406 * Update an existing template fragment. 407 * 408 * @param templateId ID of the template to which the fragment belongs. 409 * 410 * @param fragmentId ID of the fragment to update. 411 * 412 * @param text New text for the fragment. 413 * There are 4 reserved variables available to use: ${code}, ${brand}, ${time-limit} and ${time-limit-unit}. 414 * 415 * @return The updated fragment metadata. 416 * 417 * @throws VerifyResponseException If the fragment could not be updated. This could be for the following reasons: 418 * <ul> 419 * <li><b>401</b>: Invalid credentials.</li> 420 * <li><b>402</b>: Low balance.</li> 421 * <li><b>403</b>: Template management is not enabled for your account.</li> 422 * <li><b>404</b>: Fragment not found for the provided IDs.</li> 423 * <li><b>409</b>: Fragment for this channel and locale already exists.</li> 424 * <li><b>422</b>: Invalid parameters (e.g. unsupported locale or invalid text variables).</li> 425 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 426 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 427 * </ul> 428 * 429 * @since 8.13.0 430 */ 431 public TemplateFragment updateTemplateFragment(UUID templateId, UUID fragmentId, String text) { 432 return updateFragment.execute(new TemplateFragment( 433 text, validateTemplateId(templateId), validateFragmentId(fragmentId) 434 )); 435 } 436 437 /** 438 * Delete a template fragment. 439 * 440 * @param templateId ID of the template to which the fragment belongs. 441 * @param fragmentId ID of the fragment to delete. 442 * 443 * @throws VerifyResponseException If the fragment could not be deleted. This could be for the following reasons: 444 * <ul> 445 * <li><b>401</b>: Invalid credentials.</li> 446 * <li><b>402</b>: Low balance.</li> 447 * <li><b>403</b>: Template management is not enabled for your account.</li> 448 * <li><b>404</b>: Fragment not found for the provided IDs.</li> 449 * <li><b>409</b>: Fragment is in use by a template or is the default.</li> 450 * <li><b>429</b>: Rate limit hit. Please wait and try again.</li> 451 * <li><b>500</b>: An error occurred on the Vonage platform.</li> 452 * </ul> 453 * 454 * @since 8.13.0 455 */ 456 public void deleteTemplateFragment(UUID templateId, UUID fragmentId) { 457 deleteFragment.execute(new TemplateFragmentRequestWrapper( 458 validateTemplateId(templateId), 459 validateFragmentId(fragmentId) 460 )); 461 } 462}