From ffe3d4786319bc532ff2359131373dbfce60c44a Mon Sep 17 00:00:00 2001 From: Antoine Sein Date: Tue, 28 Nov 2023 15:52:09 +0100 Subject: [PATCH 1/5] Demo app chaining request from the Numbers API --- demo/pom.xml | 77 +++++ .../com/sinch/demo/NumbersSampleFlow.java | 281 ++++++++++++++++++ demo/src/main/java/com/sinch/demo/Utils.java | 42 +++ demo/src/main/resources/config.properties | 4 + demo/src/main/resources/logging.properties | 5 + 5 files changed, 409 insertions(+) create mode 100644 demo/pom.xml create mode 100644 demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java create mode 100644 demo/src/main/java/com/sinch/demo/Utils.java create mode 100644 demo/src/main/resources/config.properties create mode 100644 demo/src/main/resources/logging.properties diff --git a/demo/pom.xml b/demo/pom.xml new file mode 100644 index 00000000..bebd054f --- /dev/null +++ b/demo/pom.xml @@ -0,0 +1,77 @@ + + 4.0.0 + + com.sinch.sdk + sinch-sdk-java-demo + 1.0.0-SNAPSHOT + + jar + Sinch Java SDK Demo Application + + + [0.0.0,) + 17 + 17 + true + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.0 + + 10 + 10 + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + sinch-sdk-java-demo + false + + + com.sinch.demo.NumbersSampleFlow + + + + + + make-assembly + package + + single + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + + org.slf4j + slf4j-nop + 2.0.9 + + + + + + diff --git a/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java b/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java new file mode 100644 index 00000000..b404f826 --- /dev/null +++ b/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java @@ -0,0 +1,281 @@ +package com.sinch.demo; + +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.core.exceptions.ApiException; +import com.sinch.sdk.domains.numbers.models.*; +import com.sinch.sdk.domains.numbers.models.requests.*; +import com.sinch.sdk.domains.numbers.models.responses.ActiveNumberListResponse; +import com.sinch.sdk.domains.numbers.models.responses.AvailableNumberListResponse; +import com.sinch.sdk.domains.numbers.models.responses.AvailableRegionListResponse; +import com.sinch.sdk.models.Configuration; + +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class NumbersSampleFlow { + private static final String SINCH_PROJECT_ID = "SINCH_PROJECT_ID"; + private static final String SINCH_KEY_ID = "SINCH_KEY_ID"; + private static final String SINCH_KEY_SECRET = "SINCH_KEY_SECRET"; + + private static final Logger LOGGER = Utils.initializeLogger(NumbersSampleFlow.class.getName()); + + public static void main(String[] args) { + Properties properties = Utils.loadProperties(LOGGER); + + if (!properties.isEmpty()) { + String projectId = properties.getProperty(SINCH_PROJECT_ID); + String keyId = properties.getProperty(SINCH_KEY_ID); + String keySecret = properties.getProperty(SINCH_KEY_SECRET); + + Configuration configuration = Configuration.builder() + .setProjectId(projectId) + .setKeyId(keyId) + .setKeySecret(keySecret).build(); + + new NumbersSampleFlow().run(configuration); + } else { + LOGGER.severe("Failed to load the properties from config.properties"); + } + } + + public void run(Configuration configuration) { + + SinchClient sinch = new SinchClient(configuration); + + // ===================================================================== \\ + // Scenario: We want to rent a number of type 'LOCAL' in the 'US' region \\ + // ===================================================================== \\ + + System.out.println("+----------------------------------------------------------------------------------------------------+"); + System.out.println("| Step 1: List the available regions that support the type 'LOCAL' and check is 'US' is part of them |"); + System.out.println("+----------------------------------------------------------------------------------------------------+"); + + // Build the request data for listing the regions that support 'LOCAL' numbers + var availableRegionsRequestData = AvailableRegionListAllRequestParameters.builder() + .setTypes(Collections.singletonList(NumberType.LOCAL)) + .build(); + + // Declare the variable holding the response + AvailableRegionListResponse availableRegionListResponse; + try { + // Send the HTTP request using the built-in SDK method + availableRegionListResponse = sinch.numbers().regions().list(availableRegionsRequestData); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + // Even if not paginated, this API is following the SDK pattern for list and the server response is wrapped inside a ListResponse + // The actual response is found using the getContent() method + var regions = availableRegionListResponse.getContent(); + + // Check whether we have some results and stop the program otherwise + if (regions.isEmpty()) { + LOGGER.severe("No regions are available for the number type " + availableRegionsRequestData.getTypes().orElseThrow().stream().map(NumberType::valueOf).collect(Collectors.joining(", "))); + return; + } + + // Check if 'US' is part of the regions that support the 'LOCAL' type + System.out.println("List of available regions: " + regions.stream().map(Region::getRegionCode).collect(Collectors.joining(", "))); + String regionCode = ""; + if (regions.stream().anyMatch(region -> "US".equals(region.getRegionCode()))) { + regionCode = "US"; + System.out.println("The region '" + regionCode + "' is available. Let's use it in the following of this program.\n"); + } else { + LOGGER.severe("The 'US' region is not supported."); + } + + System.out.println("+---------------------------------------------------+"); + System.out.println("| Step 2: List available numbers in the '" + regionCode + "' region |"); + System.out.println("+---------------------------------------------------+"); + + // Build the request data for listing the numbers of type 'LOCAL' in the 'US' region + var availableNumbersRequestData = AvailableNumberListAllRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setNumberPattern(NumberPattern.builder() + .setSearchPattern(SearchPattern.START) + .setPattern("+1781").build()) // Boston area code + .build(); + + // Declare the variable holding the response + AvailableNumberListResponse availableNumbersListResponse; + try { + // Send the HTTP request using the built-in SDK method + availableNumbersListResponse = sinch.numbers().available().list(availableNumbersRequestData); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + // Even if not paginated, this API is following the SDK pattern for list and the server response is wrapped inside a ListResponse + // The actual response is found using the getContent() method + var availableNumbers = availableNumbersListResponse.getContent(); + + // Check whether we have some results and stop the program otherwise + if (availableNumbers.isEmpty()) { + LOGGER.severe("No numbers are available for the number type " + availableNumbersRequestData.getType() + " in the region " + availableNumbersRequestData.getRegionCode() + " with the pattern " + availableNumbersRequestData.getNumberPattern().get().getPattern()); + return; + } + + // Store the first phone number of the response + AvailableNumber availableNumber = availableNumbers.stream().findFirst().orElseThrow(); + String phoneNumber = availableNumber.getPhoneNumber(); + System.out.println("The phone number " + phoneNumber + " is available. Let's rent it!\n"); + + System.out.println("+-------------------------------+"); + System.out.println("| Step 3: Rent the phone number |"); + System.out.println("+-------------------------------+"); + + var rentRequestData = AvailableNumberRentRequestParameters.builder() + .build(); + + // Declare the variable holding the response + ActiveNumber rentedNumber; + try { + // Send the HTTP request using the built-in SDK method + rentedNumber = sinch.numbers().available().rent(phoneNumber, rentRequestData); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + System.out.println("The number " + rentedNumber.getPhoneNumber() + " has been rented. The next charge date is " + rentedNumber.getNextChargeDate()); + System.out.println("Let's use now the 'ActiveNumbers' API to see how to manage this number.\n"); + + // NOTE: the 2 calls above can be merged in one with 'rentAny` operation. Feel free to uncomment the following code and try it out +// var rentAnyNumberRequestData = AvailableNumberRentAnyRequestParameters.builder() +// .setRegionCode(regionCode) +// .setType(NumberType.LOCAL) +// .setNumberPattern(NumberPattern.builder() +// .setSearchPattern(SearchPattern.START) +// .setPattern("+1781").build()) +// .build(); +// +// ActiveNumber rentedAnyNumber; +// try { +// rentedAnyNumber = sinch.numbers().available().rentAny(rentAnyNumberRequestData); +// } catch (ApiException apiException) { +// LOGGER.severe(apiException.toString()); +// return; +// } +// +// System.out.println("The number " + rentedAnyNumber.getPhoneNumber() + " has been rented with the 'rentAny' operation. The next charge date is " + rentedAnyNumber.getNextChargeDate() + "\n"); + + System.out.println("+----------------------------------------------------------------------------------------------+"); + System.out.println("| Step 4: Verify the number is part of our active numbers - Method 1: Give the number in input |"); + System.out.println("+----------------------------------------------------------------------------------------------+"); + + // Declare the variable holding the response + ActiveNumber activeNumber; + try { + // Send the HTTP request using the built-in SDK method + activeNumber = sinch.numbers().active().get(phoneNumber); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + System.out.println("SUCCESS: The number " + phoneNumber + " has been found as an active number. Cost is " + activeNumber.getMoney().getAmount() + " " + activeNumber.getMoney().getCurrencyCode() + "\n"); + + System.out.println("+--------------------------------------------------------------------------------------------+"); + System.out.println("| Step 4: Check the number is part of our active numbers - Method 2: List all active numbers |"); + System.out.println("+--------------------------------------------------------------------------------------------+"); + + var listActiveNumbersRequestData = ActiveNumberListRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setPageSize(2) // Set a low number to demonstrate the pagination later on + .build(); + + // Declare the variable holding the response + ActiveNumberListResponse activeNumbersListResponse; + try { + // Send the HTTP request using the built-in SDK method + activeNumbersListResponse = sinch.numbers().active().list(listActiveNumbersRequestData); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + // First possibility to check is the phone number is part of the active numbers: check the page content and if not present, check the next page until we find the phone number, or we reach the end of the list + boolean found = false; + // The 'list' operation supports pagination: the response content is wrapped inside the 'ListResponse' interface + List activeNumbersList = (List) activeNumbersListResponse.getContent(); + int numbersListSize = activeNumbersList.size(); + + for (int i = 0; i < numbersListSize; i++) { + if (phoneNumber.equals(activeNumbersList.get(i).getPhoneNumber())) { + found = true; + break; + } else { + // Check if we are at the end of the current page and if there are more pages to load from the server + if (i == numbersListSize - 1 && activeNumbersListResponse.hasNextPage()) { + // If yes, load more active numbers: 'nextPage()' will perform an HTTP call to fetch more active numbers + activeNumbersListResponse = activeNumbersListResponse.nextPage(); + // Reset the elements of the loop to continue searching for the phone number + activeNumbersList = (List) activeNumbersListResponse.getContent(); + numbersListSize = activeNumbersList.size(); + i = -1; + } + } + } + + System.out.println("Active numbers search - first method using standard pagination"); + System.out.println("The number " + phoneNumber + " is " + (found ? "" : "NOT ") + "listed in the active numbers."); + + // Second possibility to check if the phone number is part of the active numbers: use the auto-iterator. Be careful, HTTP calls will be done under the hood without explicit mention in the source code + activeNumbersListResponse = sinch.numbers().active().list(listActiveNumbersRequestData); + var activeNumbersIterator = activeNumbersListResponse.autoPageIter(); + found = false; + while (activeNumbersIterator.hasNext()) { + var number = activeNumbersIterator.next(); // If we are at the end of the iterator content, the next() method will perform an HTTP call to load more elements in the iterator + if (phoneNumber.equals(number.getPhoneNumber())) { + found = true; + break; + } + } + + System.out.println("Active numbers search - second method using the iterator"); + System.out.println("The number " + phoneNumber + " is " + (found ? "" : "NOT ") + "listed in the active numbers.\n"); + + System.out.println("+---------------------------------+"); + System.out.println("| Step 5: Update an active number |"); + System.out.println("+---------------------------------+"); + + var updateActiveNumberRequestData = ActiveNumberUpdateRequestParameters.builder() + .setDisplayName("Rented in the scope of testing the Java SDK") + .build(); + + // Declare the variable holding the response + ActiveNumber updatedNumber; + try { + // Send the HTTP request using the built-in SDK method + updatedNumber = sinch.numbers().active().update(phoneNumber, updateActiveNumberRequestData); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + System.out.println("SUCCESS: The number " + phoneNumber + " has been updated; the display name is '" + updatedNumber.getDisplayName() + "'\n"); + + System.out.println("+----------------------------------+"); + System.out.println("| Step 6: Release the phone number |"); + System.out.println("+----------------------------------+"); + + ActiveNumber releasedNumber; + try { + // Send the HTTP request using the built-in SDK method + releasedNumber = sinch.numbers().active().release(phoneNumber); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + return; + } + + System.out.println("SUCCESS: The number " + phoneNumber + " has been released; it will expire at " + releasedNumber.getExpireAt()); + + } +} diff --git a/demo/src/main/java/com/sinch/demo/Utils.java b/demo/src/main/java/com/sinch/demo/Utils.java new file mode 100644 index 00000000..2d6997d8 --- /dev/null +++ b/demo/src/main/java/com/sinch/demo/Utils.java @@ -0,0 +1,42 @@ +package com.sinch.demo; + +import com.sinch.sdk.core.exceptions.ApiException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +public class Utils { + + public static Logger initializeLogger(String className) { + try (InputStream logConfigInputStream = NumbersSampleFlow.class.getClassLoader().getResourceAsStream("logging.properties")) { + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException("The file 'logging.properties' couldn't be loaded."); + } + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(className); + } + + public static Properties loadProperties(Logger logger) { + Properties properties = new Properties(); + + try (InputStream input = NumbersSampleFlow.class.getClassLoader().getResourceAsStream("config.properties")) { + if (input != null) { + properties.load(input); + } else { + logger.severe("'config.properties' file could not be loaded"); + } + } catch (IOException e) { + logger.severe("Error loading properties from 'config.properties'"); + } + + return properties; + } + +} diff --git a/demo/src/main/resources/config.properties b/demo/src/main/resources/config.properties new file mode 100644 index 00000000..b2eac485 --- /dev/null +++ b/demo/src/main/resources/config.properties @@ -0,0 +1,4 @@ +# supersede default values from Sinch SDK +SINCH_KEY_ID= +SINCH_KEY_SECRET= +SINCH_PROJECT_ID= diff --git a/demo/src/main/resources/logging.properties b/demo/src/main/resources/logging.properties new file mode 100644 index 00000000..ef93bfb1 --- /dev/null +++ b/demo/src/main/resources/logging.properties @@ -0,0 +1,5 @@ +handlers= java.util.logging.ConsoleHandler +.level= INFO +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s %2$s] %5$s %n From 7d52fdaffdd35ecd06f3686901d18d3d5afae385 Mon Sep 17 00:00:00 2001 From: Antoine Sein Date: Tue, 28 Nov 2023 16:23:02 +0100 Subject: [PATCH 2/5] Apply spotless --- .../com/sinch/demo/NumbersSampleFlow.java | 230 ++++++++++++------ demo/src/main/java/com/sinch/demo/Utils.java | 9 +- 2 files changed, 158 insertions(+), 81 deletions(-) diff --git a/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java b/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java index b404f826..bdeb5392 100644 --- a/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java +++ b/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java @@ -8,7 +8,6 @@ import com.sinch.sdk.domains.numbers.models.responses.AvailableNumberListResponse; import com.sinch.sdk.domains.numbers.models.responses.AvailableRegionListResponse; import com.sinch.sdk.models.Configuration; - import java.util.Collections; import java.util.List; import java.util.Properties; @@ -30,10 +29,12 @@ public static void main(String[] args) { String keyId = properties.getProperty(SINCH_KEY_ID); String keySecret = properties.getProperty(SINCH_KEY_SECRET); - Configuration configuration = Configuration.builder() - .setProjectId(projectId) - .setKeyId(keyId) - .setKeySecret(keySecret).build(); + Configuration configuration = + Configuration.builder() + .setProjectId(projectId) + .setKeyId(keyId) + .setKeySecret(keySecret) + .build(); new NumbersSampleFlow().run(configuration); } else { @@ -49,14 +50,19 @@ public void run(Configuration configuration) { // Scenario: We want to rent a number of type 'LOCAL' in the 'US' region \\ // ===================================================================== \\ - System.out.println("+----------------------------------------------------------------------------------------------------+"); - System.out.println("| Step 1: List the available regions that support the type 'LOCAL' and check is 'US' is part of them |"); - System.out.println("+----------------------------------------------------------------------------------------------------+"); + System.out.println( + "+----------------------------------------------------------------------------------------------------+"); + System.out.println( + "| Step 1: List the available regions that support the type 'LOCAL' and check is 'US' is" + + " part of them |"); + System.out.println( + "+----------------------------------------------------------------------------------------------------+"); // Build the request data for listing the regions that support 'LOCAL' numbers - var availableRegionsRequestData = AvailableRegionListAllRequestParameters.builder() - .setTypes(Collections.singletonList(NumberType.LOCAL)) - .build(); + var availableRegionsRequestData = + AvailableRegionListAllRequestParameters.builder() + .setTypes(Collections.singletonList(NumberType.LOCAL)) + .build(); // Declare the variable holding the response AvailableRegionListResponse availableRegionListResponse; @@ -68,22 +74,32 @@ public void run(Configuration configuration) { return; } - // Even if not paginated, this API is following the SDK pattern for list and the server response is wrapped inside a ListResponse + // Even if not paginated, this API is following the SDK pattern for list and the server response + // is wrapped inside a ListResponse // The actual response is found using the getContent() method var regions = availableRegionListResponse.getContent(); // Check whether we have some results and stop the program otherwise if (regions.isEmpty()) { - LOGGER.severe("No regions are available for the number type " + availableRegionsRequestData.getTypes().orElseThrow().stream().map(NumberType::valueOf).collect(Collectors.joining(", "))); + LOGGER.severe( + "No regions are available for the number type " + + availableRegionsRequestData.getTypes().orElseThrow().stream() + .map(NumberType::valueOf) + .collect(Collectors.joining(", "))); return; } // Check if 'US' is part of the regions that support the 'LOCAL' type - System.out.println("List of available regions: " + regions.stream().map(Region::getRegionCode).collect(Collectors.joining(", "))); + System.out.println( + "List of available regions: " + + regions.stream().map(Region::getRegionCode).collect(Collectors.joining(", "))); String regionCode = ""; if (regions.stream().anyMatch(region -> "US".equals(region.getRegionCode()))) { regionCode = "US"; - System.out.println("The region '" + regionCode + "' is available. Let's use it in the following of this program.\n"); + System.out.println( + "The region '" + + regionCode + + "' is available. Let's use it in the following of this program.\n"); } else { LOGGER.severe("The 'US' region is not supported."); } @@ -93,13 +109,16 @@ public void run(Configuration configuration) { System.out.println("+---------------------------------------------------+"); // Build the request data for listing the numbers of type 'LOCAL' in the 'US' region - var availableNumbersRequestData = AvailableNumberListAllRequestParameters.builder() - .setRegionCode(regionCode) - .setType(NumberType.LOCAL) - .setNumberPattern(NumberPattern.builder() - .setSearchPattern(SearchPattern.START) - .setPattern("+1781").build()) // Boston area code - .build(); + var availableNumbersRequestData = + AvailableNumberListAllRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setNumberPattern( + NumberPattern.builder() + .setSearchPattern(SearchPattern.START) + .setPattern("+1781") + .build()) // Boston area code + .build(); // Declare the variable holding the response AvailableNumberListResponse availableNumbersListResponse; @@ -111,13 +130,20 @@ public void run(Configuration configuration) { return; } - // Even if not paginated, this API is following the SDK pattern for list and the server response is wrapped inside a ListResponse + // Even if not paginated, this API is following the SDK pattern for list and the server response + // is wrapped inside a ListResponse // The actual response is found using the getContent() method var availableNumbers = availableNumbersListResponse.getContent(); // Check whether we have some results and stop the program otherwise if (availableNumbers.isEmpty()) { - LOGGER.severe("No numbers are available for the number type " + availableNumbersRequestData.getType() + " in the region " + availableNumbersRequestData.getRegionCode() + " with the pattern " + availableNumbersRequestData.getNumberPattern().get().getPattern()); + LOGGER.severe( + "No numbers are available for the number type " + + availableNumbersRequestData.getType() + + " in the region " + + availableNumbersRequestData.getRegionCode() + + " with the pattern " + + availableNumbersRequestData.getNumberPattern().get().getPattern()); return; } @@ -130,8 +156,7 @@ public void run(Configuration configuration) { System.out.println("| Step 3: Rent the phone number |"); System.out.println("+-------------------------------+"); - var rentRequestData = AvailableNumberRentRequestParameters.builder() - .build(); + var rentRequestData = AvailableNumberRentRequestParameters.builder().build(); // Declare the variable holding the response ActiveNumber rentedNumber; @@ -143,31 +168,42 @@ public void run(Configuration configuration) { return; } - System.out.println("The number " + rentedNumber.getPhoneNumber() + " has been rented. The next charge date is " + rentedNumber.getNextChargeDate()); + System.out.println( + "The number " + + rentedNumber.getPhoneNumber() + + " has been rented. The next charge date is " + + rentedNumber.getNextChargeDate()); System.out.println("Let's use now the 'ActiveNumbers' API to see how to manage this number.\n"); - // NOTE: the 2 calls above can be merged in one with 'rentAny` operation. Feel free to uncomment the following code and try it out -// var rentAnyNumberRequestData = AvailableNumberRentAnyRequestParameters.builder() -// .setRegionCode(regionCode) -// .setType(NumberType.LOCAL) -// .setNumberPattern(NumberPattern.builder() -// .setSearchPattern(SearchPattern.START) -// .setPattern("+1781").build()) -// .build(); -// -// ActiveNumber rentedAnyNumber; -// try { -// rentedAnyNumber = sinch.numbers().available().rentAny(rentAnyNumberRequestData); -// } catch (ApiException apiException) { -// LOGGER.severe(apiException.toString()); -// return; -// } -// -// System.out.println("The number " + rentedAnyNumber.getPhoneNumber() + " has been rented with the 'rentAny' operation. The next charge date is " + rentedAnyNumber.getNextChargeDate() + "\n"); - - System.out.println("+----------------------------------------------------------------------------------------------+"); - System.out.println("| Step 4: Verify the number is part of our active numbers - Method 1: Give the number in input |"); - System.out.println("+----------------------------------------------------------------------------------------------+"); + // NOTE: the 2 calls above can be merged in one with 'rentAny` operation. Feel free to uncomment + // the following code and try it out + // var rentAnyNumberRequestData = AvailableNumberRentAnyRequestParameters.builder() + // .setRegionCode(regionCode) + // .setType(NumberType.LOCAL) + // .setNumberPattern(NumberPattern.builder() + // .setSearchPattern(SearchPattern.START) + // .setPattern("+1781").build()) + // .build(); + // + // ActiveNumber rentedAnyNumber; + // try { + // rentedAnyNumber = sinch.numbers().available().rentAny(rentAnyNumberRequestData); + // } catch (ApiException apiException) { + // LOGGER.severe(apiException.toString()); + // return; + // } + // + // System.out.println("The number " + rentedAnyNumber.getPhoneNumber() + " has been rented + // with the 'rentAny' operation. The next charge date is " + rentedAnyNumber.getNextChargeDate() + // + "\n"); + + System.out.println( + "+----------------------------------------------------------------------------------------------+"); + System.out.println( + "| Step 4: Verify the number is part of our active numbers - Method 1: Give the number in" + + " input |"); + System.out.println( + "+----------------------------------------------------------------------------------------------+"); // Declare the variable holding the response ActiveNumber activeNumber; @@ -179,17 +215,29 @@ public void run(Configuration configuration) { return; } - System.out.println("SUCCESS: The number " + phoneNumber + " has been found as an active number. Cost is " + activeNumber.getMoney().getAmount() + " " + activeNumber.getMoney().getCurrencyCode() + "\n"); - - System.out.println("+--------------------------------------------------------------------------------------------+"); - System.out.println("| Step 4: Check the number is part of our active numbers - Method 2: List all active numbers |"); - System.out.println("+--------------------------------------------------------------------------------------------+"); - - var listActiveNumbersRequestData = ActiveNumberListRequestParameters.builder() - .setRegionCode(regionCode) - .setType(NumberType.LOCAL) - .setPageSize(2) // Set a low number to demonstrate the pagination later on - .build(); + System.out.println( + "SUCCESS: The number " + + phoneNumber + + " has been found as an active number. Cost is " + + activeNumber.getMoney().getAmount() + + " " + + activeNumber.getMoney().getCurrencyCode() + + "\n"); + + System.out.println( + "+--------------------------------------------------------------------------------------------+"); + System.out.println( + "| Step 4: Check the number is part of our active numbers - Method 2: List all active" + + " numbers |"); + System.out.println( + "+--------------------------------------------------------------------------------------------+"); + + var listActiveNumbersRequestData = + ActiveNumberListRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setPageSize(2) // Set a low number to demonstrate the pagination later on + .build(); // Declare the variable holding the response ActiveNumberListResponse activeNumbersListResponse; @@ -201,10 +249,14 @@ public void run(Configuration configuration) { return; } - // First possibility to check is the phone number is part of the active numbers: check the page content and if not present, check the next page until we find the phone number, or we reach the end of the list + // First possibility to check is the phone number is part of the active numbers: check the page + // content and if not present, check the next page until we find the phone number, or we reach + // the end of the list boolean found = false; - // The 'list' operation supports pagination: the response content is wrapped inside the 'ListResponse' interface - List activeNumbersList = (List) activeNumbersListResponse.getContent(); + // The 'list' operation supports pagination: the response content is wrapped inside the + // 'ListResponse' interface + List activeNumbersList = + (List) activeNumbersListResponse.getContent(); int numbersListSize = activeNumbersList.size(); for (int i = 0; i < numbersListSize; i++) { @@ -212,9 +264,11 @@ public void run(Configuration configuration) { found = true; break; } else { - // Check if we are at the end of the current page and if there are more pages to load from the server + // Check if we are at the end of the current page and if there are more pages to load from + // the server if (i == numbersListSize - 1 && activeNumbersListResponse.hasNextPage()) { - // If yes, load more active numbers: 'nextPage()' will perform an HTTP call to fetch more active numbers + // If yes, load more active numbers: 'nextPage()' will perform an HTTP call to fetch more + // active numbers activeNumbersListResponse = activeNumbersListResponse.nextPage(); // Reset the elements of the loop to continue searching for the phone number activeNumbersList = (List) activeNumbersListResponse.getContent(); @@ -225,14 +279,24 @@ public void run(Configuration configuration) { } System.out.println("Active numbers search - first method using standard pagination"); - System.out.println("The number " + phoneNumber + " is " + (found ? "" : "NOT ") + "listed in the active numbers."); - - // Second possibility to check if the phone number is part of the active numbers: use the auto-iterator. Be careful, HTTP calls will be done under the hood without explicit mention in the source code + System.out.println( + "The number " + + phoneNumber + + " is " + + (found ? "" : "NOT ") + + "listed in the active numbers."); + + // Second possibility to check if the phone number is part of the active numbers: use the + // auto-iterator. Be careful, HTTP calls will be done under the hood without explicit mention in + // the source code activeNumbersListResponse = sinch.numbers().active().list(listActiveNumbersRequestData); var activeNumbersIterator = activeNumbersListResponse.autoPageIter(); found = false; while (activeNumbersIterator.hasNext()) { - var number = activeNumbersIterator.next(); // If we are at the end of the iterator content, the next() method will perform an HTTP call to load more elements in the iterator + var number = + activeNumbersIterator + .next(); // If we are at the end of the iterator content, the next() method will + // perform an HTTP call to load more elements in the iterator if (phoneNumber.equals(number.getPhoneNumber())) { found = true; break; @@ -240,15 +304,21 @@ public void run(Configuration configuration) { } System.out.println("Active numbers search - second method using the iterator"); - System.out.println("The number " + phoneNumber + " is " + (found ? "" : "NOT ") + "listed in the active numbers.\n"); + System.out.println( + "The number " + + phoneNumber + + " is " + + (found ? "" : "NOT ") + + "listed in the active numbers.\n"); System.out.println("+---------------------------------+"); System.out.println("| Step 5: Update an active number |"); System.out.println("+---------------------------------+"); - var updateActiveNumberRequestData = ActiveNumberUpdateRequestParameters.builder() - .setDisplayName("Rented in the scope of testing the Java SDK") - .build(); + var updateActiveNumberRequestData = + ActiveNumberUpdateRequestParameters.builder() + .setDisplayName("Rented in the scope of testing the Java SDK") + .build(); // Declare the variable holding the response ActiveNumber updatedNumber; @@ -260,7 +330,12 @@ public void run(Configuration configuration) { return; } - System.out.println("SUCCESS: The number " + phoneNumber + " has been updated; the display name is '" + updatedNumber.getDisplayName() + "'\n"); + System.out.println( + "SUCCESS: The number " + + phoneNumber + + " has been updated; the display name is '" + + updatedNumber.getDisplayName() + + "'\n"); System.out.println("+----------------------------------+"); System.out.println("| Step 6: Release the phone number |"); @@ -275,7 +350,10 @@ public void run(Configuration configuration) { return; } - System.out.println("SUCCESS: The number " + phoneNumber + " has been released; it will expire at " + releasedNumber.getExpireAt()); - + System.out.println( + "SUCCESS: The number " + + phoneNumber + + " has been released; it will expire at " + + releasedNumber.getExpireAt()); } } diff --git a/demo/src/main/java/com/sinch/demo/Utils.java b/demo/src/main/java/com/sinch/demo/Utils.java index 2d6997d8..f881edd7 100644 --- a/demo/src/main/java/com/sinch/demo/Utils.java +++ b/demo/src/main/java/com/sinch/demo/Utils.java @@ -1,7 +1,5 @@ package com.sinch.demo; -import com.sinch.sdk.core.exceptions.ApiException; - import java.io.IOException; import java.io.InputStream; import java.util.Properties; @@ -11,7 +9,8 @@ public class Utils { public static Logger initializeLogger(String className) { - try (InputStream logConfigInputStream = NumbersSampleFlow.class.getClassLoader().getResourceAsStream("logging.properties")) { + try (InputStream logConfigInputStream = + NumbersSampleFlow.class.getClassLoader().getResourceAsStream("logging.properties")) { if (logConfigInputStream != null) { LogManager.getLogManager().readConfiguration(logConfigInputStream); } else { @@ -26,7 +25,8 @@ public static Logger initializeLogger(String className) { public static Properties loadProperties(Logger logger) { Properties properties = new Properties(); - try (InputStream input = NumbersSampleFlow.class.getClassLoader().getResourceAsStream("config.properties")) { + try (InputStream input = + NumbersSampleFlow.class.getClassLoader().getResourceAsStream("config.properties")) { if (input != null) { properties.load(input); } else { @@ -38,5 +38,4 @@ public static Properties loadProperties(Logger logger) { return properties; } - } From e24c55055b4a816b9ecf56843225ce635cb0a857 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier Date: Wed, 29 Nov 2023 15:52:36 +0100 Subject: [PATCH 3/5] feat: Adding helper for auto-pagination: getting a Stream --- .../sdk/core/models/pagination/ListResponse.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/com/sinch/sdk/core/models/pagination/ListResponse.java b/core/src/main/com/sinch/sdk/core/models/pagination/ListResponse.java index cfdf40ff..1dfaf8c1 100644 --- a/core/src/main/com/sinch/sdk/core/models/pagination/ListResponse.java +++ b/core/src/main/com/sinch/sdk/core/models/pagination/ListResponse.java @@ -4,6 +4,8 @@ import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * Abstract class used for handling unified paginated response @@ -50,6 +52,19 @@ public Iterator iterator() { return new ItemsIterator<>(this); } + /** + * Getting a stream across all items + * + *

Underline API (HTTP request) will be called on demand when next page content will be + * required to fulfill iterator with items for consecutive page + * + * @return Stream onto items + */ + public Stream stream() { + Iterable iterable = this::iterator; + return StreamSupport.stream(iterable.spliterator(), false); + } + static class ItemsIterator implements Iterator { ListResponse response; From 2e1f895a74683722c2a491e61bd044b9c7355d67 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier Date: Wed, 29 Nov 2023 15:47:01 +0100 Subject: [PATCH 4/5] Move demo for NumbersSampleFlow onto sample-app directory Share utility functions with other sample classes refactor NumbersSampleFlow --- demo/pom.xml | 77 ---- .../com/sinch/demo/NumbersSampleFlow.java | 359 ---------------- demo/src/main/java/com/sinch/demo/Utils.java | 41 -- demo/src/main/resources/config.properties | 4 - demo/src/main/resources/logging.properties | 5 - sample-app/README.md | 5 + sample-app/pom.xml | 7 +- .../com/sinch/sample/BaseApplication.java | 66 +-- .../src/main/java/com/sinch/sample/Utils.java | 68 +++ .../sample/numbers/NumbersSampleFlow.java | 392 ++++++++++++++++++ .../sinch/sample/numbers/available/List.java | 36 -- .../com/sinch/sample/sms/batches/Send.java | 4 +- 12 files changed, 474 insertions(+), 590 deletions(-) delete mode 100644 demo/pom.xml delete mode 100644 demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java delete mode 100644 demo/src/main/java/com/sinch/demo/Utils.java delete mode 100644 demo/src/main/resources/config.properties delete mode 100644 demo/src/main/resources/logging.properties create mode 100644 sample-app/src/main/java/com/sinch/sample/Utils.java create mode 100644 sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java diff --git a/demo/pom.xml b/demo/pom.xml deleted file mode 100644 index bebd054f..00000000 --- a/demo/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - 4.0.0 - - com.sinch.sdk - sinch-sdk-java-demo - 1.0.0-SNAPSHOT - - jar - Sinch Java SDK Demo Application - - - [0.0.0,) - 17 - 17 - true - true - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.0 - - 10 - 10 - - - - org.apache.maven.plugins - maven-assembly-plugin - - - jar-with-dependencies - - sinch-sdk-java-demo - false - - - com.sinch.demo.NumbersSampleFlow - - - - - - make-assembly - package - - single - - - - - - - - - - com.sinch.sdk - sinch-sdk-java - ${sinch.sdk.java.version} - - - - - org.slf4j - slf4j-nop - 2.0.9 - - - - - - diff --git a/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java b/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java deleted file mode 100644 index bdeb5392..00000000 --- a/demo/src/main/java/com/sinch/demo/NumbersSampleFlow.java +++ /dev/null @@ -1,359 +0,0 @@ -package com.sinch.demo; - -import com.sinch.sdk.SinchClient; -import com.sinch.sdk.core.exceptions.ApiException; -import com.sinch.sdk.domains.numbers.models.*; -import com.sinch.sdk.domains.numbers.models.requests.*; -import com.sinch.sdk.domains.numbers.models.responses.ActiveNumberListResponse; -import com.sinch.sdk.domains.numbers.models.responses.AvailableNumberListResponse; -import com.sinch.sdk.domains.numbers.models.responses.AvailableRegionListResponse; -import com.sinch.sdk.models.Configuration; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -public class NumbersSampleFlow { - private static final String SINCH_PROJECT_ID = "SINCH_PROJECT_ID"; - private static final String SINCH_KEY_ID = "SINCH_KEY_ID"; - private static final String SINCH_KEY_SECRET = "SINCH_KEY_SECRET"; - - private static final Logger LOGGER = Utils.initializeLogger(NumbersSampleFlow.class.getName()); - - public static void main(String[] args) { - Properties properties = Utils.loadProperties(LOGGER); - - if (!properties.isEmpty()) { - String projectId = properties.getProperty(SINCH_PROJECT_ID); - String keyId = properties.getProperty(SINCH_KEY_ID); - String keySecret = properties.getProperty(SINCH_KEY_SECRET); - - Configuration configuration = - Configuration.builder() - .setProjectId(projectId) - .setKeyId(keyId) - .setKeySecret(keySecret) - .build(); - - new NumbersSampleFlow().run(configuration); - } else { - LOGGER.severe("Failed to load the properties from config.properties"); - } - } - - public void run(Configuration configuration) { - - SinchClient sinch = new SinchClient(configuration); - - // ===================================================================== \\ - // Scenario: We want to rent a number of type 'LOCAL' in the 'US' region \\ - // ===================================================================== \\ - - System.out.println( - "+----------------------------------------------------------------------------------------------------+"); - System.out.println( - "| Step 1: List the available regions that support the type 'LOCAL' and check is 'US' is" - + " part of them |"); - System.out.println( - "+----------------------------------------------------------------------------------------------------+"); - - // Build the request data for listing the regions that support 'LOCAL' numbers - var availableRegionsRequestData = - AvailableRegionListAllRequestParameters.builder() - .setTypes(Collections.singletonList(NumberType.LOCAL)) - .build(); - - // Declare the variable holding the response - AvailableRegionListResponse availableRegionListResponse; - try { - // Send the HTTP request using the built-in SDK method - availableRegionListResponse = sinch.numbers().regions().list(availableRegionsRequestData); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - // Even if not paginated, this API is following the SDK pattern for list and the server response - // is wrapped inside a ListResponse - // The actual response is found using the getContent() method - var regions = availableRegionListResponse.getContent(); - - // Check whether we have some results and stop the program otherwise - if (regions.isEmpty()) { - LOGGER.severe( - "No regions are available for the number type " - + availableRegionsRequestData.getTypes().orElseThrow().stream() - .map(NumberType::valueOf) - .collect(Collectors.joining(", "))); - return; - } - - // Check if 'US' is part of the regions that support the 'LOCAL' type - System.out.println( - "List of available regions: " - + regions.stream().map(Region::getRegionCode).collect(Collectors.joining(", "))); - String regionCode = ""; - if (regions.stream().anyMatch(region -> "US".equals(region.getRegionCode()))) { - regionCode = "US"; - System.out.println( - "The region '" - + regionCode - + "' is available. Let's use it in the following of this program.\n"); - } else { - LOGGER.severe("The 'US' region is not supported."); - } - - System.out.println("+---------------------------------------------------+"); - System.out.println("| Step 2: List available numbers in the '" + regionCode + "' region |"); - System.out.println("+---------------------------------------------------+"); - - // Build the request data for listing the numbers of type 'LOCAL' in the 'US' region - var availableNumbersRequestData = - AvailableNumberListAllRequestParameters.builder() - .setRegionCode(regionCode) - .setType(NumberType.LOCAL) - .setNumberPattern( - NumberPattern.builder() - .setSearchPattern(SearchPattern.START) - .setPattern("+1781") - .build()) // Boston area code - .build(); - - // Declare the variable holding the response - AvailableNumberListResponse availableNumbersListResponse; - try { - // Send the HTTP request using the built-in SDK method - availableNumbersListResponse = sinch.numbers().available().list(availableNumbersRequestData); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - // Even if not paginated, this API is following the SDK pattern for list and the server response - // is wrapped inside a ListResponse - // The actual response is found using the getContent() method - var availableNumbers = availableNumbersListResponse.getContent(); - - // Check whether we have some results and stop the program otherwise - if (availableNumbers.isEmpty()) { - LOGGER.severe( - "No numbers are available for the number type " - + availableNumbersRequestData.getType() - + " in the region " - + availableNumbersRequestData.getRegionCode() - + " with the pattern " - + availableNumbersRequestData.getNumberPattern().get().getPattern()); - return; - } - - // Store the first phone number of the response - AvailableNumber availableNumber = availableNumbers.stream().findFirst().orElseThrow(); - String phoneNumber = availableNumber.getPhoneNumber(); - System.out.println("The phone number " + phoneNumber + " is available. Let's rent it!\n"); - - System.out.println("+-------------------------------+"); - System.out.println("| Step 3: Rent the phone number |"); - System.out.println("+-------------------------------+"); - - var rentRequestData = AvailableNumberRentRequestParameters.builder().build(); - - // Declare the variable holding the response - ActiveNumber rentedNumber; - try { - // Send the HTTP request using the built-in SDK method - rentedNumber = sinch.numbers().available().rent(phoneNumber, rentRequestData); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - System.out.println( - "The number " - + rentedNumber.getPhoneNumber() - + " has been rented. The next charge date is " - + rentedNumber.getNextChargeDate()); - System.out.println("Let's use now the 'ActiveNumbers' API to see how to manage this number.\n"); - - // NOTE: the 2 calls above can be merged in one with 'rentAny` operation. Feel free to uncomment - // the following code and try it out - // var rentAnyNumberRequestData = AvailableNumberRentAnyRequestParameters.builder() - // .setRegionCode(regionCode) - // .setType(NumberType.LOCAL) - // .setNumberPattern(NumberPattern.builder() - // .setSearchPattern(SearchPattern.START) - // .setPattern("+1781").build()) - // .build(); - // - // ActiveNumber rentedAnyNumber; - // try { - // rentedAnyNumber = sinch.numbers().available().rentAny(rentAnyNumberRequestData); - // } catch (ApiException apiException) { - // LOGGER.severe(apiException.toString()); - // return; - // } - // - // System.out.println("The number " + rentedAnyNumber.getPhoneNumber() + " has been rented - // with the 'rentAny' operation. The next charge date is " + rentedAnyNumber.getNextChargeDate() - // + "\n"); - - System.out.println( - "+----------------------------------------------------------------------------------------------+"); - System.out.println( - "| Step 4: Verify the number is part of our active numbers - Method 1: Give the number in" - + " input |"); - System.out.println( - "+----------------------------------------------------------------------------------------------+"); - - // Declare the variable holding the response - ActiveNumber activeNumber; - try { - // Send the HTTP request using the built-in SDK method - activeNumber = sinch.numbers().active().get(phoneNumber); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - System.out.println( - "SUCCESS: The number " - + phoneNumber - + " has been found as an active number. Cost is " - + activeNumber.getMoney().getAmount() - + " " - + activeNumber.getMoney().getCurrencyCode() - + "\n"); - - System.out.println( - "+--------------------------------------------------------------------------------------------+"); - System.out.println( - "| Step 4: Check the number is part of our active numbers - Method 2: List all active" - + " numbers |"); - System.out.println( - "+--------------------------------------------------------------------------------------------+"); - - var listActiveNumbersRequestData = - ActiveNumberListRequestParameters.builder() - .setRegionCode(regionCode) - .setType(NumberType.LOCAL) - .setPageSize(2) // Set a low number to demonstrate the pagination later on - .build(); - - // Declare the variable holding the response - ActiveNumberListResponse activeNumbersListResponse; - try { - // Send the HTTP request using the built-in SDK method - activeNumbersListResponse = sinch.numbers().active().list(listActiveNumbersRequestData); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - // First possibility to check is the phone number is part of the active numbers: check the page - // content and if not present, check the next page until we find the phone number, or we reach - // the end of the list - boolean found = false; - // The 'list' operation supports pagination: the response content is wrapped inside the - // 'ListResponse' interface - List activeNumbersList = - (List) activeNumbersListResponse.getContent(); - int numbersListSize = activeNumbersList.size(); - - for (int i = 0; i < numbersListSize; i++) { - if (phoneNumber.equals(activeNumbersList.get(i).getPhoneNumber())) { - found = true; - break; - } else { - // Check if we are at the end of the current page and if there are more pages to load from - // the server - if (i == numbersListSize - 1 && activeNumbersListResponse.hasNextPage()) { - // If yes, load more active numbers: 'nextPage()' will perform an HTTP call to fetch more - // active numbers - activeNumbersListResponse = activeNumbersListResponse.nextPage(); - // Reset the elements of the loop to continue searching for the phone number - activeNumbersList = (List) activeNumbersListResponse.getContent(); - numbersListSize = activeNumbersList.size(); - i = -1; - } - } - } - - System.out.println("Active numbers search - first method using standard pagination"); - System.out.println( - "The number " - + phoneNumber - + " is " - + (found ? "" : "NOT ") - + "listed in the active numbers."); - - // Second possibility to check if the phone number is part of the active numbers: use the - // auto-iterator. Be careful, HTTP calls will be done under the hood without explicit mention in - // the source code - activeNumbersListResponse = sinch.numbers().active().list(listActiveNumbersRequestData); - var activeNumbersIterator = activeNumbersListResponse.autoPageIter(); - found = false; - while (activeNumbersIterator.hasNext()) { - var number = - activeNumbersIterator - .next(); // If we are at the end of the iterator content, the next() method will - // perform an HTTP call to load more elements in the iterator - if (phoneNumber.equals(number.getPhoneNumber())) { - found = true; - break; - } - } - - System.out.println("Active numbers search - second method using the iterator"); - System.out.println( - "The number " - + phoneNumber - + " is " - + (found ? "" : "NOT ") - + "listed in the active numbers.\n"); - - System.out.println("+---------------------------------+"); - System.out.println("| Step 5: Update an active number |"); - System.out.println("+---------------------------------+"); - - var updateActiveNumberRequestData = - ActiveNumberUpdateRequestParameters.builder() - .setDisplayName("Rented in the scope of testing the Java SDK") - .build(); - - // Declare the variable holding the response - ActiveNumber updatedNumber; - try { - // Send the HTTP request using the built-in SDK method - updatedNumber = sinch.numbers().active().update(phoneNumber, updateActiveNumberRequestData); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - System.out.println( - "SUCCESS: The number " - + phoneNumber - + " has been updated; the display name is '" - + updatedNumber.getDisplayName() - + "'\n"); - - System.out.println("+----------------------------------+"); - System.out.println("| Step 6: Release the phone number |"); - System.out.println("+----------------------------------+"); - - ActiveNumber releasedNumber; - try { - // Send the HTTP request using the built-in SDK method - releasedNumber = sinch.numbers().active().release(phoneNumber); - } catch (ApiException apiException) { - LOGGER.severe(apiException.toString()); - return; - } - - System.out.println( - "SUCCESS: The number " - + phoneNumber - + " has been released; it will expire at " - + releasedNumber.getExpireAt()); - } -} diff --git a/demo/src/main/java/com/sinch/demo/Utils.java b/demo/src/main/java/com/sinch/demo/Utils.java deleted file mode 100644 index f881edd7..00000000 --- a/demo/src/main/java/com/sinch/demo/Utils.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sinch.demo; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -public class Utils { - - public static Logger initializeLogger(String className) { - try (InputStream logConfigInputStream = - NumbersSampleFlow.class.getClassLoader().getResourceAsStream("logging.properties")) { - if (logConfigInputStream != null) { - LogManager.getLogManager().readConfiguration(logConfigInputStream); - } else { - throw new RuntimeException("The file 'logging.properties' couldn't be loaded."); - } - } catch (IOException e) { - throw new RuntimeException(e.getMessage()); - } - return Logger.getLogger(className); - } - - public static Properties loadProperties(Logger logger) { - Properties properties = new Properties(); - - try (InputStream input = - NumbersSampleFlow.class.getClassLoader().getResourceAsStream("config.properties")) { - if (input != null) { - properties.load(input); - } else { - logger.severe("'config.properties' file could not be loaded"); - } - } catch (IOException e) { - logger.severe("Error loading properties from 'config.properties'"); - } - - return properties; - } -} diff --git a/demo/src/main/resources/config.properties b/demo/src/main/resources/config.properties deleted file mode 100644 index b2eac485..00000000 --- a/demo/src/main/resources/config.properties +++ /dev/null @@ -1,4 +0,0 @@ -# supersede default values from Sinch SDK -SINCH_KEY_ID= -SINCH_KEY_SECRET= -SINCH_PROJECT_ID= diff --git a/demo/src/main/resources/logging.properties b/demo/src/main/resources/logging.properties deleted file mode 100644 index ef93bfb1..00000000 --- a/demo/src/main/resources/logging.properties +++ /dev/null @@ -1,5 +0,0 @@ -handlers= java.util.logging.ConsoleHandler -.level= INFO -java.util.logging.ConsoleHandler.level = INFO -java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter -java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s %2$s] %5$s %n diff --git a/sample-app/README.md b/sample-app/README.md index 6690df83..2769d783 100644 --- a/sample-app/README.md +++ b/sample-app/README.md @@ -49,6 +49,11 @@ See https://developers.sinch.com for details about these parameters ## Available samples classes +### Full workflow +A full application chaining calls to Numbers service to onboard onto Java SDK and Numbers: [NumbersSampleFlow](src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java) + +### Dedicated service feature samples + | API | Service | Sample | Class | Notes | |---------|----------------|------------------------|---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------| | Numbers | Available | - CheckAvailability | [com.sinch.sample.numbers.available.CheckAvailability](src/main/java/com/sinch/sample/numbers/available/CheckAvailability.java) | Require `PHONE_NUMBER` parameter | diff --git a/sample-app/pom.xml b/sample-app/pom.xml index c1aa0ce1..859bef44 100644 --- a/sample-app/pom.xml +++ b/sample-app/pom.xml @@ -12,11 +12,8 @@ [0.0.0,) - 1.8 - 1.8 - 1.8 - 1.8 - 8 + 11 + 11 3.8.0 true true diff --git a/sample-app/src/main/java/com/sinch/sample/BaseApplication.java b/sample-app/src/main/java/com/sinch/sample/BaseApplication.java index 7ac80671..e114e055 100644 --- a/sample-app/src/main/java/com/sinch/sample/BaseApplication.java +++ b/sample-app/src/main/java/com/sinch/sample/BaseApplication.java @@ -2,29 +2,16 @@ import com.sinch.sdk.SinchClient; import com.sinch.sdk.models.Configuration; -import com.sinch.sdk.models.SMSRegion; import java.io.IOException; -import java.io.InputStream; import java.util.Properties; -import java.util.logging.LogManager; import java.util.logging.Logger; public abstract class BaseApplication { - /** duplicate of SinchClient property files for lisibility facility but could be anything else */ - private static final String OAUTH_URL_KEY = "oauth-url"; - - private static final String NUMBERS_SERVER_KEY = "numbers-server"; - private static final String SMS_REGION_KEY = "sms-region"; - private static final String SMS_SERVER_KEY = "sms-server"; - private static final String BATCH_ID_KEY = "BATCH_ID"; private static final String PHONE_NUMBER_KEY = "PHONE_NUMBER"; - private static final String SINCH_KEY_ID = "SINCH_KEY_ID"; - private static final String SINCH_KEY_SECRET = "SINCH_KEY_SECRET"; - private static final String SINCH_PROJECT_ID = "SINCH_PROJECT_ID"; - protected static Logger LOGGER; + protected static final Logger LOGGER = Utils.initializeLogger(BaseApplication.class.getName()); protected SinchClient client; protected String phoneNumber; @@ -32,36 +19,11 @@ public abstract class BaseApplication { protected BaseApplication() throws IOException { - Properties properties = handleConfigurations(); - - String keyId = - null != System.getenv(SINCH_KEY_ID) - ? System.getenv(SINCH_KEY_ID) - : properties.getProperty(SINCH_KEY_ID); - String keySecret = - null != System.getenv(SINCH_KEY_SECRET) - ? System.getenv(SINCH_KEY_SECRET) - : properties.getProperty(SINCH_KEY_SECRET); - String projectId = - null != System.getenv(SINCH_PROJECT_ID) - ? System.getenv(SINCH_PROJECT_ID) - : properties.getProperty(SINCH_PROJECT_ID); - - Configuration.Builder builder = - Configuration.builder().setKeyId(keyId).setKeySecret(keySecret).setProjectId(projectId); - LOGGER.info("Starting application"); - // override by local config - if (properties.containsKey(NUMBERS_SERVER_KEY)) { - builder.setNumbersUrl(properties.getProperty(NUMBERS_SERVER_KEY)); - } - if (properties.containsKey(SMS_SERVER_KEY)) { - builder.setSmsUrl(properties.getProperty(SMS_SERVER_KEY)); - } - if (properties.containsKey(SMS_REGION_KEY)) { - builder.setSmsRegion(SMSRegion.from(properties.getProperty(SMS_REGION_KEY))); - } + Configuration configuration = Utils.loadConfiguration(LOGGER); + + Properties properties = Utils.loadProperties(LOGGER); phoneNumber = null != System.getenv(PHONE_NUMBER_KEY) ? System.getenv(PHONE_NUMBER_KEY) @@ -71,25 +33,7 @@ protected BaseApplication() throws IOException { ? System.getenv(BATCH_ID_KEY) : properties.getProperty(BATCH_ID_KEY); - client = new SinchClient(builder.build()); - } - - private static Properties handleConfigurations() throws IOException { - - InputStream is = BaseApplication.class.getResourceAsStream("/logging.properties"); - if (null != is) { - LogManager.getLogManager().readConfiguration(is); - is.close(); - } - LOGGER = Logger.getLogger(BaseApplication.class.getName()); - - Properties prop = new Properties(); - is = BaseApplication.class.getResourceAsStream("/config.properties"); - if (null != is) { - prop.load(is); - is.close(); - } - return prop; + client = new SinchClient(configuration); } public abstract void run(); diff --git a/sample-app/src/main/java/com/sinch/sample/Utils.java b/sample-app/src/main/java/com/sinch/sample/Utils.java new file mode 100644 index 00000000..51e1297a --- /dev/null +++ b/sample-app/src/main/java/com/sinch/sample/Utils.java @@ -0,0 +1,68 @@ +package com.sinch.sample; + +import com.sinch.sdk.models.Configuration; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +public class Utils { + + private static final String SINCH_KEY_ID = "SINCH_KEY_ID"; + private static final String SINCH_KEY_SECRET = "SINCH_KEY_SECRET"; + private static final String SINCH_PROJECT_ID = "SINCH_PROJECT_ID"; + + public static Logger initializeLogger(String className) { + try (InputStream logConfigInputStream = + Utils.class.getClassLoader().getResourceAsStream("logging.properties")) { + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException("The file 'logging.properties' couldn't be loaded."); + } + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(className); + } + + public static Properties loadProperties(Logger logger) { + Properties properties = new Properties(); + + try (InputStream input = + Utils.class.getClassLoader().getResourceAsStream("config.properties")) { + if (input != null) { + properties.load(input); + } else { + logger.severe("'config.properties' file could not be loaded"); + } + } catch (IOException e) { + logger.severe("Error loading properties from 'config.properties'"); + } + return properties; + } + + public static Configuration loadConfiguration(Logger logger) { + Properties properties = loadProperties(logger); + + String keyId = + null != System.getenv(SINCH_KEY_ID) + ? System.getenv(SINCH_KEY_ID) + : properties.getProperty(SINCH_KEY_ID); + String keySecret = + null != System.getenv(SINCH_KEY_SECRET) + ? System.getenv(SINCH_KEY_SECRET) + : properties.getProperty(SINCH_KEY_SECRET); + String projectId = + null != System.getenv(SINCH_PROJECT_ID) + ? System.getenv(SINCH_PROJECT_ID) + : properties.getProperty(SINCH_PROJECT_ID); + + return Configuration.builder() + .setKeyId(keyId) + .setKeySecret(keySecret) + .setProjectId(projectId) + .build(); + } +} diff --git a/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java b/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java new file mode 100644 index 00000000..a276a5c7 --- /dev/null +++ b/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java @@ -0,0 +1,392 @@ +package com.sinch.sample.numbers; + +import com.sinch.sample.Utils; +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.core.exceptions.ApiException; +import com.sinch.sdk.domains.numbers.models.*; +import com.sinch.sdk.domains.numbers.models.requests.*; +import com.sinch.sdk.domains.numbers.models.responses.ActiveNumberListResponse; +import com.sinch.sdk.domains.numbers.models.responses.AvailableNumberListResponse; +import com.sinch.sdk.domains.numbers.models.responses.AvailableRegionListResponse; +import com.sinch.sdk.models.Configuration; +import java.util.Collections; +import java.util.Optional; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class NumbersSampleFlow { + + private static final String SINCH_PROJECT_ID = "SINCH_PROJECT_ID"; + private static final String SINCH_KEY_ID = "SINCH_KEY_ID"; + private static final String SINCH_KEY_SECRET = "SINCH_KEY_SECRET"; + + private static final Logger LOGGER = Utils.initializeLogger(NumbersSampleFlow.class.getName()); + + public static void main(String[] args) { + + try { + Configuration configuration = Utils.loadConfiguration(LOGGER); + new NumbersSampleFlow().run(configuration); + } catch (ApiException apiException) { + LOGGER.severe(apiException.toString()); + System.exit(-1); + } + } + + public void run(Configuration configuration) { + + String testRegionCode = "US"; // region code we are interested in + String testCodePattern = "+1781"; // Boston area code + + SinchClient sinch = new SinchClient(configuration); + // ===================================================================== \\ + // Scenario: We want to rent a number of type 'LOCAL' in the 'US' region \\ + // ===================================================================== \\ + int step = 0; + + // 1. check and retrieve region code availability + var regionCode = checkAndGetRegionCodeAvailability(++step, sinch, testRegionCode); + + // 2. retrieve a number for this region code + boolean rentByNumber = true; + String phoneNumber; + + if (rentByNumber) { + // 2.1 can be rent by phone number + phoneNumber = getAvailablePhoneNumber(++step, sinch, regionCode, testCodePattern); + // Rent by phone number + rentPhoneNumber(++step, sinch, phoneNumber); + } else { + // 2.2 or can be rent by filtering parameters + phoneNumber = rentPhoneNumberByAny(++step, sinch, regionCode, testCodePattern); + } + + // 3 Verify number is active + // 3.1: verify by dedicated phone number request + verifyActiveNumberByNumber(++step, sinch, phoneNumber); + // 3.2: verify by listing page after page and check for requested number + verifyActiveNumberByPagination(++step, sinch, phoneNumber, regionCode); + // 3.3: verify by using auto pagination feature and check for requested number + verifyActiveNumberByAutoPagination(++step, sinch, phoneNumber, regionCode); + + // 4: Update an active number + updateActiveNumber(++step, sinch, phoneNumber); + + // 5. Release the number + releasePhoneNumber(++step, sinch, phoneNumber); + } + + String checkAndGetRegionCodeAvailability(int step, SinchClient sinchClient, String regionCode) { + + echoStep( + step, + "List the available regions that support the type 'LOCAL' and check if '" + + regionCode + + "' is" + + " part of them"); + + // 1. Build the request data for listing the regions that support 'LOCAL' numbers + var availableRegionsRequestData = + AvailableRegionListAllRequestParameters.builder() + .setTypes(Collections.singletonList(NumberType.LOCAL)) + .build(); + + // 2. Request for available regions using the built-in SDK method + AvailableRegionListResponse availableRegionListResponse = + sinchClient.numbers().regions().list(availableRegionsRequestData); + + // 3. This API is following the SDK pattern for list and the server response is wrapped inside a + // ListResponse + // The response list is found using the getContent() method + var regions = availableRegionListResponse.getContent(); + // Check whether we have some results and stop the program otherwise + if (regions.isEmpty()) { + throw new ApiException( + "No regions are available for the number type " + + availableRegionsRequestData.getTypes().orElseThrow().stream() + .map(NumberType::valueOf) + .collect(Collectors.joining(", "))); + } + + // 4. auto iteration other items is available for all List responses. + // In case of underlying API is requiring new calls (paginated results): it will be handled + // automatically + echo("List of available regions:"); + availableRegionListResponse.iterator().forEachRemaining(f -> echo(" - " + f.getRegionCode())); + + // 4. Check if 'US' is part of the regions that support the 'LOCAL' type + if (availableRegionListResponse.stream() + .noneMatch(region -> regionCode.equals(region.getRegionCode()))) { + throw new ApiException("The '" + regionCode + "' region is not supported."); + } + + echo( + "The region '" + + regionCode + + "' is available. Let's use it in the following of this program.\n"); + + return regionCode; + } + + String getAvailablePhoneNumber( + int step, SinchClient sinchClient, String regionCode, String codePattern) { + + echoStep( + step, + "List available numbers in the '" + + regionCode + + "' region with pattern '" + + codePattern + + "'"); + + // 1. Build the request data for listing the numbers of type 'LOCAL' in the regionCode with an + // area code pattern + var availableNumbersRequestData = + AvailableNumberListAllRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setNumberPattern( + NumberPattern.builder() + .setSearchPattern(SearchPattern.START) + .setPattern(codePattern) + .build()) + .build(); + + // 2. Request for available numbers using the built-in SDK method + // This API is following the SDK pattern for list and the server response is wrapped inside a + AvailableNumberListResponse availableNumbersListResponse = + sinchClient.numbers().available().list(availableNumbersRequestData); + + // 3. Looking for first available number + // auto iteration other items is available for all List responses. + // In case of underlying API is requiring new calls (paginated results): it will be handled + // automatically + AvailableNumber availableNumber = + availableNumbersListResponse.stream() + .findFirst() + .orElseThrow( + () -> + new ApiException( + "No numbers are available for the number type " + + availableNumbersRequestData.getType() + + " in the region " + + availableNumbersRequestData.getRegionCode() + + " with the pattern " + + availableNumbersRequestData.getNumberPattern().get().getPattern())); + + // 4. return the phone number + String phoneNumber = availableNumber.getPhoneNumber(); + echo("The phone number '" + phoneNumber + "' is available. Let's rent it!\n"); + + return phoneNumber; + } + + void rentPhoneNumber(int step, SinchClient sinchClient, String phoneNumber) { + + echoStep(step, "Rent phone number: '" + phoneNumber + "'"); + + // 1. Build the request data + var rentRequestData = AvailableNumberRentRequestParameters.builder().build(); + + // 2. Request to rent the number + ActiveNumber rentedNumber = + sinchClient.numbers().available().rent(phoneNumber, rentRequestData); + + echo( + "The number " + + rentedNumber.getPhoneNumber() + + " has been rented with the 'rent' operation.The next charge date is " + + rentedNumber.getNextChargeDate()); + echo("Let's use now the 'ActiveNumbers' API to see how to manage this number.\n"); + } + + String rentPhoneNumberByAny( + int step, SinchClient sinchClient, String regionCode, String pattern) { + + echoStep(step, "Rent a phone number for region '" + regionCode + "'"); + + // 1. Build the request data + var rentAnyNumberRequestData = + AvailableNumberRentAnyRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setNumberPattern( + NumberPattern.builder() + .setSearchPattern(SearchPattern.START) + .setPattern(pattern) + .build()) + .build(); + + // 2. rent a number + ActiveNumber rentedNumber = sinchClient.numbers().available().rentAny(rentAnyNumberRequestData); + + echo( + "The number " + + rentedNumber.getPhoneNumber() + + " has been rented with the 'rentAny' operation.The next charge date is " + + rentedNumber.getNextChargeDate() + + "\n"); + return rentedNumber.getPhoneNumber(); + } + + void verifyActiveNumberByNumber(int step, SinchClient sinchClient, String phoneNumber) { + echoStep( + step, + "Verify the number is part of our active numbers - Method 1: Give the number in input"); + + // 1. Simple request to get information related to active number + ActiveNumber activeNumber = sinchClient.numbers().active().get(phoneNumber); + + echo( + "SUCCESS: The number " + + phoneNumber + + " has been found as an active number. Cost is " + + activeNumber.getMoney().getAmount() + + " " + + activeNumber.getMoney().getCurrencyCode() + + "\n"); + } + + void verifyActiveNumberByPagination( + int step, SinchClient sinchClient, String phoneNumber, String regionCode) { + + echoStep( + step, + "Check the number is part of our active numbers - Method 2: List all active numbers and" + + " navigate across pages"); + + // 1. Build the request data + var listActiveNumbersRequestData = + ActiveNumberListRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setPageSize(2) // Set a low number to demonstrate the pagination later on + .build(); + + // 2. Request for active number using the built-in SDK method + ActiveNumberListResponse activeNumbersListResponse = + sinchClient.numbers().active().list(listActiveNumbersRequestData); + + // 3. To check if the phone number is part of the active numbers: check the page + // content and if not present, check the next page until we find the phone number, or we reach + // the end of the list + Optional found; + do { + found = + activeNumbersListResponse.getContent().stream() + .filter( + f -> + phoneNumber.equals(f.getPhoneNumber()) + && regionCode.equals(f.getRegionCode())) + .findFirst(); + if (found.isEmpty() && activeNumbersListResponse.hasNextPage()) { + activeNumbersListResponse = activeNumbersListResponse.nextPage(); + } else { + activeNumbersListResponse = null; + } + } while (found.isEmpty() && null != activeNumbersListResponse); + + echo( + (found.isPresent() ? "SUCCESS:" : "FAILED:") + + " The number " + + phoneNumber + + " is " + + (found.isPresent() ? "" : "NOT ") + + "listed in the active numbers.\n"); + } + + void verifyActiveNumberByAutoPagination( + int step, SinchClient sinchClient, String phoneNumber, String regionCode) { + + echoStep( + step, + "Check the number is part of our active numbers - Method 3: List all active numbers and" + + " navigate across pages using auto pagination SDK feature"); + + // 1. Build the request data + var listActiveNumbersRequestData = + ActiveNumberListRequestParameters.builder() + .setRegionCode(regionCode) + .setType(NumberType.LOCAL) + .setPageSize(2) // Set a low number to demonstrate the pagination later on + .build(); + + // 2. Request for active number using the built-in SDK method + ActiveNumberListResponse activeNumbersListResponse = + sinchClient.numbers().active().list(listActiveNumbersRequestData); + + // 3. To check if the phone number is part of the active numbers: use the + // auto-iterator. Like for hasNextPage/nextPage, http calls will be performed automatically when + // required + activeNumbersListResponse = sinchClient.numbers().active().list(listActiveNumbersRequestData); + var iterator = activeNumbersListResponse.iterator(); + boolean found = false; + while (iterator.hasNext()) { + var number = + iterator + .next(); // If we are at the end of the current page content, the next() method will + // perform an HTTP call to load more elements in the iterator + if (phoneNumber.equals(number.getPhoneNumber()) + && regionCode.equals(number.getRegionCode())) { + found = true; + break; + } + } + + echo( + (found ? "SUCCESS:" : "FAILED:") + + " The number " + + phoneNumber + + " is " + + (found ? "" : "NOT ") + + "listed in the active numbers.\n"); + } + + void updateActiveNumber(int step, SinchClient sinchClient, String phoneNumber) { + + echoStep(step, "Update active number: '" + phoneNumber + "'"); + + // 1. Build the request data + var updateActiveNumberRequestData = + ActiveNumberUpdateRequestParameters.builder() + .setDisplayName("Rented in the scope of testing the Java SDK") + .build(); + + // 2. Request to update the number + ActiveNumber updatedNumber = + sinchClient.numbers().active().update(phoneNumber, updateActiveNumberRequestData); + + echo( + "SUCCESS: The number " + + phoneNumber + + " has been updated; the display name is '" + + updatedNumber.getDisplayName() + + "'\n"); + } + + void releasePhoneNumber(int step, SinchClient sinchClient, String phoneNumber) { + echoStep(step, "Releasing phone number: '" + phoneNumber + "'"); + + // 1. Request to release the number + ActiveNumber releasedNumber = sinchClient.numbers().active().release(phoneNumber); + + echo( + "SUCCESS: The number " + + phoneNumber + + " has been released; it will expire at " + + releasedNumber.getExpireAt()); + } + + void echoStep(int step, String text) { + String formatted = "| Step " + step + ": " + text + " |"; + String separator = String.format("+%0" + (formatted.length() - 2) + "d+", 0).replace('0', '-'); + + System.out.println(separator); + System.out.println(formatted); + System.out.println(separator); + } + + void echo(String text) { + System.out.println(" " + text); + } +} diff --git a/sample-app/src/main/java/com/sinch/sample/numbers/available/List.java b/sample-app/src/main/java/com/sinch/sample/numbers/available/List.java index 67970a46..1ff7f50d 100644 --- a/sample-app/src/main/java/com/sinch/sample/numbers/available/List.java +++ b/sample-app/src/main/java/com/sinch/sample/numbers/available/List.java @@ -1,11 +1,8 @@ package com.sinch.sample.numbers.available; import com.sinch.sample.BaseApplication; -import com.sinch.sdk.core.exceptions.ApiException; import com.sinch.sdk.domains.numbers.models.Capability; -import com.sinch.sdk.domains.numbers.models.NumberPattern; import com.sinch.sdk.domains.numbers.models.NumberType; -import com.sinch.sdk.domains.numbers.models.SearchPattern; import com.sinch.sdk.domains.numbers.models.requests.AvailableNumberListAllRequestParameters; import com.sinch.sdk.domains.numbers.models.responses.AvailableNumberListResponse; import java.io.IOException; @@ -48,38 +45,5 @@ public void run() { page++; LOGGER.info(String.format("Response (page %d): %s", page, response)); } - - try { - LOGGER.info("List with error"); - page = 1; - response = - client - .numbers() - .available() - .list( - AvailableNumberListAllRequestParameters.builder() - .setRegionCode("SE") - // this will throw an error but from server side as an invalid parameter: we - // can pass new values (or private) - .setType(NumberType.from("foo")) - .setNumberPattern( - NumberPattern.builder() - .setPattern("93652") - .setSearchPattern(SearchPattern.CONTAINS) - .build()) - .setCapabilities(Collections.singletonList(Capability.SMS)) - .setSize(3) - .build()); - LOGGER.info(String.format("Response (page %d): %s", page, response)); - - while (response.hasNextPage()) { - response = response.nextPage(); - page++; - LOGGER.info(String.format("Response (page %d): %s", page, response)); - } - - } catch (ApiException e) { - LOGGER.severe("Error: " + e); - } } } diff --git a/sample-app/src/main/java/com/sinch/sample/sms/batches/Send.java b/sample-app/src/main/java/com/sinch/sample/sms/batches/Send.java index afa44d61..03286734 100644 --- a/sample-app/src/main/java/com/sinch/sample/sms/batches/Send.java +++ b/sample-app/src/main/java/com/sinch/sample/sms/batches/Send.java @@ -23,14 +23,14 @@ public static void main(String[] args) { public void run() { - LOGGER.info("Send Text"); + LOGGER.info("Send Text to " + phoneNumber); BatchText value = client .sms() .batches() .send( SendSmsBatchTextRequest.builder() - .setTo(Collections.singletonList("+foo ")) + .setTo(Collections.singletonList(phoneNumber)) .setBody("the body") .setClientReference("a client reference") .setFrom("+33123456789") From 1e2391e5857b864fca9230642c6fd0db1ec2ec29 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier Date: Thu, 30 Nov 2023 08:14:45 +0100 Subject: [PATCH 5/5] feat: PR comments --- .../adapters/AvailableNumberService.java | 6 ++- .../sample/numbers/NumbersSampleFlow.java | 39 ++++++++++--------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/client/src/main/com/sinch/sdk/domains/numbers/adapters/AvailableNumberService.java b/client/src/main/com/sinch/sdk/domains/numbers/adapters/AvailableNumberService.java index f158c800..1ce0e924 100644 --- a/client/src/main/com/sinch/sdk/domains/numbers/adapters/AvailableNumberService.java +++ b/client/src/main/com/sinch/sdk/domains/numbers/adapters/AvailableNumberService.java @@ -88,12 +88,16 @@ public AvailableNumber checkAvailability(String phoneNumber) throws ApiException public ActiveNumber rent(String phoneNumber, AvailableNumberRentRequestParameters parameters) throws ApiException { + + AvailableNumberRentRequestParameters guardParameters = + null != parameters ? parameters : AvailableNumberRentRequestParameters.builder().build(); + ActiveNumberDto response = getApi() .numberServiceRentNumber( configuration.getProjectId(), phoneNumber, - AvailableRentRequestParametersDtoConverter.convert(parameters)); + AvailableRentRequestParametersDtoConverter.convert(guardParameters)); return ActiveNumberDtoConverter.convert(response); } diff --git a/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java b/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java index a276a5c7..35cd7f90 100644 --- a/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java +++ b/sample-app/src/main/java/com/sinch/sample/numbers/NumbersSampleFlow.java @@ -5,9 +5,6 @@ import com.sinch.sdk.core.exceptions.ApiException; import com.sinch.sdk.domains.numbers.models.*; import com.sinch.sdk.domains.numbers.models.requests.*; -import com.sinch.sdk.domains.numbers.models.responses.ActiveNumberListResponse; -import com.sinch.sdk.domains.numbers.models.responses.AvailableNumberListResponse; -import com.sinch.sdk.domains.numbers.models.responses.AvailableRegionListResponse; import com.sinch.sdk.models.Configuration; import java.util.Collections; import java.util.Optional; @@ -16,10 +13,6 @@ public class NumbersSampleFlow { - private static final String SINCH_PROJECT_ID = "SINCH_PROJECT_ID"; - private static final String SINCH_KEY_ID = "SINCH_KEY_ID"; - private static final String SINCH_KEY_SECRET = "SINCH_KEY_SECRET"; - private static final Logger LOGGER = Utils.initializeLogger(NumbersSampleFlow.class.getName()); public static void main(String[] args) { @@ -61,6 +54,8 @@ public void run(Configuration configuration) { phoneNumber = rentPhoneNumberByAny(++step, sinch, regionCode, testCodePattern); } + echo("Let's use now the 'ActiveNumbers' API to see how to manage this number.\n"); + // 3 Verify number is active // 3.1: verify by dedicated phone number request verifyActiveNumberByNumber(++step, sinch, phoneNumber); @@ -92,7 +87,7 @@ String checkAndGetRegionCodeAvailability(int step, SinchClient sinchClient, Stri .build(); // 2. Request for available regions using the built-in SDK method - AvailableRegionListResponse availableRegionListResponse = + var availableRegionListResponse = sinchClient.numbers().regions().list(availableRegionsRequestData); // 3. This API is following the SDK pattern for list and the server response is wrapped inside a @@ -154,7 +149,7 @@ String getAvailablePhoneNumber( // 2. Request for available numbers using the built-in SDK method // This API is following the SDK pattern for list and the server response is wrapped inside a - AvailableNumberListResponse availableNumbersListResponse = + var availableNumbersListResponse = sinchClient.numbers().available().list(availableNumbersRequestData); // 3. Looking for first available number @@ -186,7 +181,8 @@ void rentPhoneNumber(int step, SinchClient sinchClient, String phoneNumber) { echoStep(step, "Rent phone number: '" + phoneNumber + "'"); // 1. Build the request data - var rentRequestData = AvailableNumberRentRequestParameters.builder().build(); + var rentRequestData = + AvailableNumberRentRequestParameters.builder().setCallbackUrl("https://foo.url").build(); // 2. Request to rent the number ActiveNumber rentedNumber = @@ -197,7 +193,6 @@ void rentPhoneNumber(int step, SinchClient sinchClient, String phoneNumber) { + rentedNumber.getPhoneNumber() + " has been rented with the 'rent' operation.The next charge date is " + rentedNumber.getNextChargeDate()); - echo("Let's use now the 'ActiveNumbers' API to see how to manage this number.\n"); } String rentPhoneNumberByAny( @@ -264,14 +259,16 @@ void verifyActiveNumberByPagination( .build(); // 2. Request for active number using the built-in SDK method - ActiveNumberListResponse activeNumbersListResponse = + var activeNumbersListResponse = sinchClient.numbers().active().list(listActiveNumbersRequestData); // 3. To check if the phone number is part of the active numbers: check the page // content and if not present, check the next page until we find the phone number, or we reach // the end of the list Optional found; + boolean reachedEndOfPages = false; do { + // use stream onto page content to looking for phoneNumber found = activeNumbersListResponse.getContent().stream() .filter( @@ -279,12 +276,17 @@ void verifyActiveNumberByPagination( phoneNumber.equals(f.getPhoneNumber()) && regionCode.equals(f.getRegionCode())) .findFirst(); - if (found.isEmpty() && activeNumbersListResponse.hasNextPage()) { - activeNumbersListResponse = activeNumbersListResponse.nextPage(); - } else { - activeNumbersListResponse = null; + + // not present within current page + if (found.isEmpty()) { + // need to load the next one... if there is one available + if (activeNumbersListResponse.hasNextPage()) { + activeNumbersListResponse = activeNumbersListResponse.nextPage(); + } else { + reachedEndOfPages = true; + } } - } while (found.isEmpty() && null != activeNumbersListResponse); + } while (found.isEmpty() && !reachedEndOfPages); echo( (found.isPresent() ? "SUCCESS:" : "FAILED:") @@ -312,13 +314,12 @@ void verifyActiveNumberByAutoPagination( .build(); // 2. Request for active number using the built-in SDK method - ActiveNumberListResponse activeNumbersListResponse = + var activeNumbersListResponse = sinchClient.numbers().active().list(listActiveNumbersRequestData); // 3. To check if the phone number is part of the active numbers: use the // auto-iterator. Like for hasNextPage/nextPage, http calls will be performed automatically when // required - activeNumbersListResponse = sinchClient.numbers().active().list(listActiveNumbersRequestData); var iterator = activeNumbersListResponse.iterator(); boolean found = false; while (iterator.hasNext()) {