diff --git a/getting-started/compile.sh b/getting-started/compile.sh index df08e87..bdd9a85 100755 --- a/getting-started/compile.sh +++ b/getting-started/compile.sh @@ -1,9 +1,15 @@ #!/bin/sh DIRECTORIES=" +conversation/send-text-message/client +numbers/rent-and-configure/client +numbers/rent-first-available-number/client +numbers/search-available/client +sms/send-sms-message/client sms/respond-to-incoming-message/server verification/user-verification-using-sms-pin/client voice/respond-to-incoming-call/server +voice/make-a-call/client " for DIRECTORY in $DIRECTORIES diff --git a/getting-started/conversation/send-text-message/client/README.md b/getting-started/conversation/send-text-message/client/README.md new file mode 100644 index 0000000..05d5fe5 --- /dev/null +++ b/getting-started/conversation/send-text-message/client/README.md @@ -0,0 +1,5 @@ +# Sinch Getting started + +Code is related to [Send a Conversation Message with the Sinch Java SDK](https://developers.sinch.com/docs/conversation/getting-started/java-sdk/send-message). + +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/conversation/send-text-message/client/pom.xml b/getting-started/conversation/send-text-message/client/pom.xml new file mode 100644 index 0000000..823a8e2 --- /dev/null +++ b/getting-started/conversation/send-text-message/client/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + my.company.com + sinch-java-sdk-client-application + 1.0-SNAPSHOT + jar + Sinch Java SDK Client Application + + + [1.5.0,) + 8 + 8 + 3.13.0 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-sources + generate-sources + + add-source + + + + src/main/java + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + Application + + + + + jar-with-dependencies + + + + + package + + single + + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + org.slf4j + slf4j-jdk14 + 2.0.9 + + + + + diff --git a/getting-started/conversation/send-text-message/client/src/main/java/Application.java b/getting-started/conversation/send-text-message/client/src/main/java/Application.java new file mode 100644 index 0000000..431d975 --- /dev/null +++ b/getting-started/conversation/send-text-message/client/src/main/java/Application.java @@ -0,0 +1,42 @@ +import com.sinch.sdk.SinchClient; +import conversation.ConversationQuickStart; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +public abstract class Application { + + private static final String LOGGING_PROPERTIES_FILE = "logging.properties"; + private static final Logger LOGGER = initializeLogger(); + + public static void main(String[] args) { + try { + + SinchClient client = SinchClientHelper.initSinchClient(); + LOGGER.info("Application initiated. SinchClient ready."); + + ConversationQuickStart conversation = new ConversationQuickStart(client.conversation().v1()); + + } catch (Exception e) { + LOGGER.severe(String.format("Application failure: %s", e.getMessage())); + } + } + + static Logger initializeLogger() { + try (InputStream logConfigInputStream = + Application.class.getClassLoader().getResourceAsStream(LOGGING_PROPERTIES_FILE)) { + + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException( + String.format("The file '%s' couldn't be loaded.", LOGGING_PROPERTIES_FILE)); + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(Application.class.getName()); + } +} diff --git a/getting-started/conversation/send-text-message/client/src/main/java/SinchClientHelper.java b/getting-started/conversation/send-text-message/client/src/main/java/SinchClientHelper.java new file mode 100644 index 0000000..fdc54f1 --- /dev/null +++ b/getting-started/conversation/send-text-message/client/src/main/java/SinchClientHelper.java @@ -0,0 +1,90 @@ +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.models.Configuration; +import com.sinch.sdk.models.ConversationRegion; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Properties; +import java.util.logging.Logger; + +public class SinchClientHelper { + + private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); + + 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 String CONVERSATION_REGION = "CONVERSATION_REGION"; + + private static final String CONFIG_FILE = "config.properties"; + + public static SinchClient initSinchClient() { + + LOGGER.info("Initializing client"); + + Configuration configuration = getConfiguration(); + + return new SinchClient(configuration); + } + + private static Configuration getConfiguration() { + + Properties properties = loadProperties(); + + Configuration.Builder builder = Configuration.builder(); + + manageUnifiedCredentials(properties, builder); + manageConversationConfiguration(properties, builder); + + return builder.build(); + } + + private static Properties loadProperties() { + + Properties properties = new Properties(); + + try (InputStream input = + SinchClientHelper.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input != null) { + properties.load(input); + } else { + LOGGER.severe(String.format("'%s' file could not be loaded", CONFIG_FILE)); + } + } catch (IOException e) { + LOGGER.severe(String.format("Error loading properties from '%s'", CONFIG_FILE)); + } + + return properties; + } + + static void manageUnifiedCredentials(Properties properties, Configuration.Builder builder) { + + Optional projectId = getConfigValue(properties, SINCH_PROJECT_ID); + Optional keyId = getConfigValue(properties, SINCH_KEY_ID); + Optional keySecret = getConfigValue(properties, SINCH_KEY_SECRET); + + projectId.ifPresent(builder::setProjectId); + keyId.ifPresent(builder::setKeyId); + keySecret.ifPresent(builder::setKeySecret); + } + + private static void manageConversationConfiguration( + Properties properties, Configuration.Builder builder) { + + Optional region = getConfigValue(properties, CONVERSATION_REGION); + + region.ifPresent(value -> builder.setConversationRegion(ConversationRegion.from(value))); + } + + private static Optional getConfigValue(Properties properties, String key) { + String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); + + // empty value means setting not set + if (null != value && value.trim().isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(value); + } +} diff --git a/getting-started/conversation/send-text-message/client/src/main/java/conversation/ConversationQuickStart.java b/getting-started/conversation/send-text-message/client/src/main/java/conversation/ConversationQuickStart.java new file mode 100644 index 0000000..f994556 --- /dev/null +++ b/getting-started/conversation/send-text-message/client/src/main/java/conversation/ConversationQuickStart.java @@ -0,0 +1,15 @@ +package conversation; + +import com.sinch.sdk.domains.conversation.api.v1.ConversationService; + +public class ConversationQuickStart { + + private final ConversationService conversationService; + + public ConversationQuickStart(ConversationService conversationService) { + this.conversationService = conversationService; + + // replace by your code and business logic + Snippet.execute(this.conversationService); + } +} diff --git a/getting-started/conversation/send-text-message/client/src/main/java/conversation/Snippet.java b/getting-started/conversation/send-text-message/client/src/main/java/conversation/Snippet.java new file mode 100644 index 0000000..773b8f8 --- /dev/null +++ b/getting-started/conversation/send-text-message/client/src/main/java/conversation/Snippet.java @@ -0,0 +1,55 @@ +package conversation; + +import com.sinch.sdk.domains.conversation.api.v1.ConversationService; +import com.sinch.sdk.domains.conversation.api.v1.MessagesService; +import com.sinch.sdk.domains.conversation.models.v1.*; +import com.sinch.sdk.domains.conversation.models.v1.messages.*; +import com.sinch.sdk.domains.conversation.models.v1.messages.request.*; +import com.sinch.sdk.domains.conversation.models.v1.messages.response.SendMessageResponse; +import com.sinch.sdk.domains.conversation.models.v1.messages.types.text.*; +import java.util.*; +import java.util.Collections; +import java.util.logging.Logger; + +public class Snippet { + + private static final Logger LOGGER = Logger.getLogger(Snippet.class.getName()); + + static void execute(ConversationService conversationService) { + + MessagesService messagesService = conversationService.messages(); + + String appId = "YOUR_app_id"; + String from = "YOUR_sms_sender"; + String to = "RECIPIENT_number"; + + String body = "This is a test Conversation message using the Sinch Java SDK."; + String smsSender = "SMS_SENDER"; + + ChannelRecipientIdentities recipients = + ChannelRecipientIdentities.of( + ChannelRecipientIdentity.builder() + .setChannel(ConversationChannel.SMS) + .setIdentity(to) + .build()); + + AppMessage message = + AppMessage.builder() + .setBody(TextMessage.builder().setText(body).build()) + .build(); + + SendMessageRequest request = + SendMessageRequest.builder() + .setAppId(appId) + .setRecipient(recipients) + .setMessage(message) + .setChannelProperties(Collections.singletonMap(smsSender, from)) + .build(); + + LOGGER.info("Sending SMS Text using Conversation API"); + + SendMessageResponse value = messagesService.sendMessage(request); + + LOGGER.info("Response: " + value); + } +} diff --git a/getting-started/conversation/send-text-message/client/src/main/resources/config.properties b/getting-started/conversation/send-text-message/client/src/main/resources/config.properties new file mode 100644 index 0000000..ea97bca --- /dev/null +++ b/getting-started/conversation/send-text-message/client/src/main/resources/config.properties @@ -0,0 +1,7 @@ +# Required credentials for using the Conversation API +SINCH_PROJECT_ID= +SINCH_KEY_ID= +SINCH_KEY_SECRET= + +# See https://github.com/sinch/sinch-sdk-java/blob/main/client/src/main/com/sinch/sdk/models/ConversationRegion.java for list of supported values +#CONVERSATION_REGION = us \ No newline at end of file diff --git a/getting-started/conversation/send-text-message/client/src/main/resources/logging.properties b/getting-started/conversation/send-text-message/client/src/main/resources/logging.properties new file mode 100644 index 0000000..5ed611e --- /dev/null +++ b/getting-started/conversation/send-text-message/client/src/main/resources/logging.properties @@ -0,0 +1,8 @@ + +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = INFO +com.sinch.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/getting-started/numbers/rent-and-configure/client/README.md b/getting-started/numbers/rent-and-configure/client/README.md new file mode 100644 index 0000000..af483df --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/README.md @@ -0,0 +1,5 @@ +# Sinch Getting started + +Code is related to [Rent and configure your virtual number using Java](https://developers.sinch.com/docs/numbers/getting-started/java-sdk/rentandconfig). + +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/numbers/rent-and-configure/client/pom.xml b/getting-started/numbers/rent-and-configure/client/pom.xml new file mode 100644 index 0000000..823a8e2 --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + my.company.com + sinch-java-sdk-client-application + 1.0-SNAPSHOT + jar + Sinch Java SDK Client Application + + + [1.5.0,) + 8 + 8 + 3.13.0 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-sources + generate-sources + + add-source + + + + src/main/java + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + Application + + + + + jar-with-dependencies + + + + + package + + single + + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + org.slf4j + slf4j-jdk14 + 2.0.9 + + + + + diff --git a/getting-started/numbers/rent-and-configure/client/src/main/java/Application.java b/getting-started/numbers/rent-and-configure/client/src/main/java/Application.java new file mode 100644 index 0000000..f029b86 --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/src/main/java/Application.java @@ -0,0 +1,42 @@ +import com.sinch.sdk.SinchClient; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import numbers.NumbersQuickStart; + +public abstract class Application { + + private static final String LOGGING_PROPERTIES_FILE = "logging.properties"; + private static final Logger LOGGER = initializeLogger(); + + public static void main(String[] args) { + try { + + SinchClient client = SinchClientHelper.initSinchClient(); + LOGGER.info("Application initiated. SinchClient ready."); + + NumbersQuickStart numbers = new NumbersQuickStart(client.numbers().v1()); + + } catch (Exception e) { + LOGGER.severe(String.format("Application failure: %s", e.getMessage())); + } + } + + static Logger initializeLogger() { + try (InputStream logConfigInputStream = + Application.class.getClassLoader().getResourceAsStream(LOGGING_PROPERTIES_FILE)) { + + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException( + String.format("The file '%s' couldn't be loaded.", LOGGING_PROPERTIES_FILE)); + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(Application.class.getName()); + } +} diff --git a/getting-started/numbers/rent-and-configure/client/src/main/java/SinchClientHelper.java b/getting-started/numbers/rent-and-configure/client/src/main/java/SinchClientHelper.java new file mode 100644 index 0000000..b454c50 --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/src/main/java/SinchClientHelper.java @@ -0,0 +1,78 @@ +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.models.Configuration; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Properties; +import java.util.logging.Logger; + +public class SinchClientHelper { + + private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); + + 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 String CONFIG_FILE = "config.properties"; + + public static SinchClient initSinchClient() { + + LOGGER.info("Initializing client"); + + Configuration configuration = getConfiguration(); + + return new SinchClient(configuration); + } + + private static Configuration getConfiguration() { + + Properties properties = loadProperties(); + + Configuration.Builder builder = Configuration.builder(); + + manageUnifiedCredentials(properties, builder); + + return builder.build(); + } + + private static Properties loadProperties() { + + Properties properties = new Properties(); + + try (InputStream input = + SinchClientHelper.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input != null) { + properties.load(input); + } else { + LOGGER.severe(String.format("'%s' file could not be loaded", CONFIG_FILE)); + } + } catch (IOException e) { + LOGGER.severe(String.format("Error loading properties from '%s'", CONFIG_FILE)); + } + + return properties; + } + + static void manageUnifiedCredentials(Properties properties, Configuration.Builder builder) { + + Optional projectId = getConfigValue(properties, SINCH_PROJECT_ID); + Optional keyId = getConfigValue(properties, SINCH_KEY_ID); + Optional keySecret = getConfigValue(properties, SINCH_KEY_SECRET); + + projectId.ifPresent(builder::setProjectId); + keyId.ifPresent(builder::setKeyId); + keySecret.ifPresent(builder::setKeySecret); + } + + private static Optional getConfigValue(Properties properties, String key) { + String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); + + // empty value means setting not set + if (null != value && value.trim().isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(value); + } +} diff --git a/getting-started/numbers/rent-and-configure/client/src/main/java/numbers/NumbersQuickStart.java b/getting-started/numbers/rent-and-configure/client/src/main/java/numbers/NumbersQuickStart.java new file mode 100644 index 0000000..f221e06 --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/src/main/java/numbers/NumbersQuickStart.java @@ -0,0 +1,15 @@ +package numbers; + +import com.sinch.sdk.domains.numbers.api.v1.NumbersService; + +public class NumbersQuickStart { + + private final NumbersService numbersService; + + public NumbersQuickStart(NumbersService numbersService) { + this.numbersService = numbersService; + + // replace by your code and business logic + Snippet.execute(this.numbersService); + } +} diff --git a/getting-started/numbers/rent-and-configure/client/src/main/java/numbers/Snippet.java b/getting-started/numbers/rent-and-configure/client/src/main/java/numbers/Snippet.java new file mode 100644 index 0000000..f460ce1 --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/src/main/java/numbers/Snippet.java @@ -0,0 +1,36 @@ +package numbers; + +import com.sinch.sdk.domains.numbers.api.v1.NumbersService; +import com.sinch.sdk.domains.numbers.models.v1.ActiveNumber; +import com.sinch.sdk.domains.numbers.models.v1.SmsConfiguration; +import com.sinch.sdk.domains.numbers.models.v1.request.AvailableNumberRentRequest; +import java.util.logging.Logger; + +public class Snippet { + + private static final Logger LOGGER = Logger.getLogger(Snippet.class.getName()); + + static void execute(NumbersService numbersService) { + + // Available numbers list can be retrieved by using list() function from available service, see: + // https://developers.sinch.com/docs/numbers/getting-started/java-sdk/searchavailable + String phoneNumber = "available_phone_number_to_be_rented"; + String servicePlanId = "YOUR_service_plan_id"; + + SmsConfiguration smsConfiguration = + SmsConfiguration.builder().setServicePlanId(servicePlanId).build(); + + LOGGER.info( + String.format( + "Sending request to rent the virtual number: '%s' and configure it with the" + + " pre-configured service plan id '%s' to use the SMS capability", + phoneNumber, servicePlanId)); + + AvailableNumberRentRequest rentRequest = + AvailableNumberRentRequest.builder().setSmsConfiguration(smsConfiguration).build(); + + ActiveNumber response = numbersService.rent(phoneNumber, rentRequest); + + LOGGER.info(String.format("Rented number: %s", response)); + } +} diff --git a/getting-started/numbers/rent-and-configure/client/src/main/resources/config.properties b/getting-started/numbers/rent-and-configure/client/src/main/resources/config.properties new file mode 100644 index 0000000..2ff29a1 --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/src/main/resources/config.properties @@ -0,0 +1,4 @@ +# Required credentials for using the Numbers API +SINCH_PROJECT_ID= +SINCH_KEY_ID= +SINCH_KEY_SECRET= diff --git a/getting-started/numbers/rent-and-configure/client/src/main/resources/logging.properties b/getting-started/numbers/rent-and-configure/client/src/main/resources/logging.properties new file mode 100644 index 0000000..5ed611e --- /dev/null +++ b/getting-started/numbers/rent-and-configure/client/src/main/resources/logging.properties @@ -0,0 +1,8 @@ + +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = INFO +com.sinch.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/getting-started/numbers/rent-first-available-number/client/README.md b/getting-started/numbers/rent-first-available-number/client/README.md new file mode 100644 index 0000000..03b738c --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/README.md @@ -0,0 +1,5 @@ +# Sinch Getting started + +Code is related to [Rent the first available number using the Java SDK](https://developers.sinch.com/docs/numbers/getting-started/java-sdk/rentany). + +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/numbers/rent-first-available-number/client/pom.xml b/getting-started/numbers/rent-first-available-number/client/pom.xml new file mode 100644 index 0000000..823a8e2 --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + my.company.com + sinch-java-sdk-client-application + 1.0-SNAPSHOT + jar + Sinch Java SDK Client Application + + + [1.5.0,) + 8 + 8 + 3.13.0 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-sources + generate-sources + + add-source + + + + src/main/java + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + Application + + + + + jar-with-dependencies + + + + + package + + single + + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + org.slf4j + slf4j-jdk14 + 2.0.9 + + + + + diff --git a/getting-started/numbers/rent-first-available-number/client/src/main/java/Application.java b/getting-started/numbers/rent-first-available-number/client/src/main/java/Application.java new file mode 100644 index 0000000..f029b86 --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/src/main/java/Application.java @@ -0,0 +1,42 @@ +import com.sinch.sdk.SinchClient; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import numbers.NumbersQuickStart; + +public abstract class Application { + + private static final String LOGGING_PROPERTIES_FILE = "logging.properties"; + private static final Logger LOGGER = initializeLogger(); + + public static void main(String[] args) { + try { + + SinchClient client = SinchClientHelper.initSinchClient(); + LOGGER.info("Application initiated. SinchClient ready."); + + NumbersQuickStart numbers = new NumbersQuickStart(client.numbers().v1()); + + } catch (Exception e) { + LOGGER.severe(String.format("Application failure: %s", e.getMessage())); + } + } + + static Logger initializeLogger() { + try (InputStream logConfigInputStream = + Application.class.getClassLoader().getResourceAsStream(LOGGING_PROPERTIES_FILE)) { + + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException( + String.format("The file '%s' couldn't be loaded.", LOGGING_PROPERTIES_FILE)); + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(Application.class.getName()); + } +} diff --git a/getting-started/numbers/rent-first-available-number/client/src/main/java/SinchClientHelper.java b/getting-started/numbers/rent-first-available-number/client/src/main/java/SinchClientHelper.java new file mode 100644 index 0000000..b454c50 --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/src/main/java/SinchClientHelper.java @@ -0,0 +1,78 @@ +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.models.Configuration; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Properties; +import java.util.logging.Logger; + +public class SinchClientHelper { + + private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); + + 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 String CONFIG_FILE = "config.properties"; + + public static SinchClient initSinchClient() { + + LOGGER.info("Initializing client"); + + Configuration configuration = getConfiguration(); + + return new SinchClient(configuration); + } + + private static Configuration getConfiguration() { + + Properties properties = loadProperties(); + + Configuration.Builder builder = Configuration.builder(); + + manageUnifiedCredentials(properties, builder); + + return builder.build(); + } + + private static Properties loadProperties() { + + Properties properties = new Properties(); + + try (InputStream input = + SinchClientHelper.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input != null) { + properties.load(input); + } else { + LOGGER.severe(String.format("'%s' file could not be loaded", CONFIG_FILE)); + } + } catch (IOException e) { + LOGGER.severe(String.format("Error loading properties from '%s'", CONFIG_FILE)); + } + + return properties; + } + + static void manageUnifiedCredentials(Properties properties, Configuration.Builder builder) { + + Optional projectId = getConfigValue(properties, SINCH_PROJECT_ID); + Optional keyId = getConfigValue(properties, SINCH_KEY_ID); + Optional keySecret = getConfigValue(properties, SINCH_KEY_SECRET); + + projectId.ifPresent(builder::setProjectId); + keyId.ifPresent(builder::setKeyId); + keySecret.ifPresent(builder::setKeySecret); + } + + private static Optional getConfigValue(Properties properties, String key) { + String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); + + // empty value means setting not set + if (null != value && value.trim().isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(value); + } +} diff --git a/getting-started/numbers/rent-first-available-number/client/src/main/java/numbers/NumbersQuickStart.java b/getting-started/numbers/rent-first-available-number/client/src/main/java/numbers/NumbersQuickStart.java new file mode 100644 index 0000000..f221e06 --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/src/main/java/numbers/NumbersQuickStart.java @@ -0,0 +1,15 @@ +package numbers; + +import com.sinch.sdk.domains.numbers.api.v1.NumbersService; + +public class NumbersQuickStart { + + private final NumbersService numbersService; + + public NumbersQuickStart(NumbersService numbersService) { + this.numbersService = numbersService; + + // replace by your code and business logic + Snippet.execute(this.numbersService); + } +} diff --git a/getting-started/numbers/rent-first-available-number/client/src/main/java/numbers/Snippet.java b/getting-started/numbers/rent-first-available-number/client/src/main/java/numbers/Snippet.java new file mode 100644 index 0000000..1deec16 --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/src/main/java/numbers/Snippet.java @@ -0,0 +1,42 @@ +package numbers; + +import com.sinch.sdk.domains.numbers.api.v1.NumbersService; +import com.sinch.sdk.domains.numbers.models.v1.ActiveNumber; +import com.sinch.sdk.domains.numbers.models.v1.Capability; +import com.sinch.sdk.domains.numbers.models.v1.NumberType; +import com.sinch.sdk.domains.numbers.models.v1.SmsConfiguration; +import com.sinch.sdk.domains.numbers.models.v1.request.AvailableNumberRentAnyRequest; +import java.util.Collections; +import java.util.logging.Logger; + +public class Snippet { + + private static final Logger LOGGER = Logger.getLogger(Snippet.class.getName()); + + static void execute(NumbersService numbersService) { + + String servicePlanId = "YOUR_service_plan_id"; + String regionCode = "YOUR_region_code"; + + Capability capability = Capability.SMS; + NumberType numberType = NumberType.LOCAL; + + LOGGER.info( + String.format( + "Sending request to rent the first available number and configure it with the" + + " pre-configured service plan id '%s' to use the SMS capability", + servicePlanId)); + + ActiveNumber response = + numbersService.rentAny( + AvailableNumberRentAnyRequest.builder() + .setCapabilities(Collections.singletonList(capability)) + .setType(numberType) + .setRegionCode(regionCode) + .setSmsConfiguration( + SmsConfiguration.builder().setServicePlanId(servicePlanId).build()) + .build()); + + LOGGER.info(String.format("Rented number: %s", response)); + } +} diff --git a/getting-started/numbers/rent-first-available-number/client/src/main/resources/config.properties b/getting-started/numbers/rent-first-available-number/client/src/main/resources/config.properties new file mode 100644 index 0000000..2ff29a1 --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/src/main/resources/config.properties @@ -0,0 +1,4 @@ +# Required credentials for using the Numbers API +SINCH_PROJECT_ID= +SINCH_KEY_ID= +SINCH_KEY_SECRET= diff --git a/getting-started/numbers/rent-first-available-number/client/src/main/resources/logging.properties b/getting-started/numbers/rent-first-available-number/client/src/main/resources/logging.properties new file mode 100644 index 0000000..5ed611e --- /dev/null +++ b/getting-started/numbers/rent-first-available-number/client/src/main/resources/logging.properties @@ -0,0 +1,8 @@ + +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = INFO +com.sinch.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/getting-started/numbers/search-available/client/README.md b/getting-started/numbers/search-available/client/README.md new file mode 100644 index 0000000..713ef77 --- /dev/null +++ b/getting-started/numbers/search-available/client/README.md @@ -0,0 +1,5 @@ +# Sinch Getting started + +Code is related to [Search for virtual number using the Java SDK](https://developers.sinch.com/docs/numbers/getting-started/java-sdk/searchavailable). + +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/numbers/search-available/client/pom.xml b/getting-started/numbers/search-available/client/pom.xml new file mode 100644 index 0000000..823a8e2 --- /dev/null +++ b/getting-started/numbers/search-available/client/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + my.company.com + sinch-java-sdk-client-application + 1.0-SNAPSHOT + jar + Sinch Java SDK Client Application + + + [1.5.0,) + 8 + 8 + 3.13.0 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-sources + generate-sources + + add-source + + + + src/main/java + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + Application + + + + + jar-with-dependencies + + + + + package + + single + + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + org.slf4j + slf4j-jdk14 + 2.0.9 + + + + + diff --git a/getting-started/numbers/search-available/client/src/main/java/Application.java b/getting-started/numbers/search-available/client/src/main/java/Application.java new file mode 100644 index 0000000..f029b86 --- /dev/null +++ b/getting-started/numbers/search-available/client/src/main/java/Application.java @@ -0,0 +1,42 @@ +import com.sinch.sdk.SinchClient; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import numbers.NumbersQuickStart; + +public abstract class Application { + + private static final String LOGGING_PROPERTIES_FILE = "logging.properties"; + private static final Logger LOGGER = initializeLogger(); + + public static void main(String[] args) { + try { + + SinchClient client = SinchClientHelper.initSinchClient(); + LOGGER.info("Application initiated. SinchClient ready."); + + NumbersQuickStart numbers = new NumbersQuickStart(client.numbers().v1()); + + } catch (Exception e) { + LOGGER.severe(String.format("Application failure: %s", e.getMessage())); + } + } + + static Logger initializeLogger() { + try (InputStream logConfigInputStream = + Application.class.getClassLoader().getResourceAsStream(LOGGING_PROPERTIES_FILE)) { + + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException( + String.format("The file '%s' couldn't be loaded.", LOGGING_PROPERTIES_FILE)); + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(Application.class.getName()); + } +} diff --git a/getting-started/numbers/search-available/client/src/main/java/SinchClientHelper.java b/getting-started/numbers/search-available/client/src/main/java/SinchClientHelper.java new file mode 100644 index 0000000..b454c50 --- /dev/null +++ b/getting-started/numbers/search-available/client/src/main/java/SinchClientHelper.java @@ -0,0 +1,78 @@ +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.models.Configuration; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Properties; +import java.util.logging.Logger; + +public class SinchClientHelper { + + private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); + + 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 String CONFIG_FILE = "config.properties"; + + public static SinchClient initSinchClient() { + + LOGGER.info("Initializing client"); + + Configuration configuration = getConfiguration(); + + return new SinchClient(configuration); + } + + private static Configuration getConfiguration() { + + Properties properties = loadProperties(); + + Configuration.Builder builder = Configuration.builder(); + + manageUnifiedCredentials(properties, builder); + + return builder.build(); + } + + private static Properties loadProperties() { + + Properties properties = new Properties(); + + try (InputStream input = + SinchClientHelper.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input != null) { + properties.load(input); + } else { + LOGGER.severe(String.format("'%s' file could not be loaded", CONFIG_FILE)); + } + } catch (IOException e) { + LOGGER.severe(String.format("Error loading properties from '%s'", CONFIG_FILE)); + } + + return properties; + } + + static void manageUnifiedCredentials(Properties properties, Configuration.Builder builder) { + + Optional projectId = getConfigValue(properties, SINCH_PROJECT_ID); + Optional keyId = getConfigValue(properties, SINCH_KEY_ID); + Optional keySecret = getConfigValue(properties, SINCH_KEY_SECRET); + + projectId.ifPresent(builder::setProjectId); + keyId.ifPresent(builder::setKeyId); + keySecret.ifPresent(builder::setKeySecret); + } + + private static Optional getConfigValue(Properties properties, String key) { + String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); + + // empty value means setting not set + if (null != value && value.trim().isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(value); + } +} diff --git a/getting-started/numbers/search-available/client/src/main/java/numbers/NumbersQuickStart.java b/getting-started/numbers/search-available/client/src/main/java/numbers/NumbersQuickStart.java new file mode 100644 index 0000000..f221e06 --- /dev/null +++ b/getting-started/numbers/search-available/client/src/main/java/numbers/NumbersQuickStart.java @@ -0,0 +1,15 @@ +package numbers; + +import com.sinch.sdk.domains.numbers.api.v1.NumbersService; + +public class NumbersQuickStart { + + private final NumbersService numbersService; + + public NumbersQuickStart(NumbersService numbersService) { + this.numbersService = numbersService; + + // replace by your code and business logic + Snippet.execute(this.numbersService); + } +} diff --git a/getting-started/numbers/search-available/client/src/main/java/numbers/Snippet.java b/getting-started/numbers/search-available/client/src/main/java/numbers/Snippet.java new file mode 100644 index 0000000..9352d65 --- /dev/null +++ b/getting-started/numbers/search-available/client/src/main/java/numbers/Snippet.java @@ -0,0 +1,31 @@ +package numbers; + +import com.sinch.sdk.domains.numbers.api.v1.NumbersService; +import com.sinch.sdk.domains.numbers.models.v1.NumberType; +import com.sinch.sdk.domains.numbers.models.v1.request.AvailableNumberListRequest; +import com.sinch.sdk.domains.numbers.models.v1.response.AvailableNumberListResponse; +import java.util.logging.Logger; + +public class Snippet { + + private static final Logger LOGGER = Logger.getLogger(Snippet.class.getName()); + + static void execute(NumbersService numbersService) { + + String regionCode = "US"; + NumberType type = NumberType.LOCAL; + + AvailableNumberListRequest parameters = + AvailableNumberListRequest.builder().setRegionCode(regionCode).setType(type).build(); + + LOGGER.info( + String.format("Listing available number type '%s' for region '%s'", type, regionCode)); + + AvailableNumberListResponse response = numbersService.searchForAvailableNumbers(parameters); + + response + .iterator() + .forEachRemaining( + number -> LOGGER.info(String.format("Available number details: %s", number))); + } +} diff --git a/getting-started/numbers/search-available/client/src/main/resources/config.properties b/getting-started/numbers/search-available/client/src/main/resources/config.properties new file mode 100644 index 0000000..2ff29a1 --- /dev/null +++ b/getting-started/numbers/search-available/client/src/main/resources/config.properties @@ -0,0 +1,4 @@ +# Required credentials for using the Numbers API +SINCH_PROJECT_ID= +SINCH_KEY_ID= +SINCH_KEY_SECRET= diff --git a/getting-started/numbers/search-available/client/src/main/resources/logging.properties b/getting-started/numbers/search-available/client/src/main/resources/logging.properties new file mode 100644 index 0000000..5ed611e --- /dev/null +++ b/getting-started/numbers/search-available/client/src/main/resources/logging.properties @@ -0,0 +1,8 @@ + +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = INFO +com.sinch.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/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/Config.java b/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/Config.java index be44878..e0404a9 100644 --- a/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/Config.java +++ b/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/Config.java @@ -4,23 +4,26 @@ import com.sinch.sdk.core.utils.StringUtil; import com.sinch.sdk.models.Configuration; import com.sinch.sdk.models.SMSRegion; +import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @org.springframework.context.annotation.Configuration public class Config { - @Value("${credentials.project-id}") + private static final Logger LOGGER = Logger.getLogger(Config.class.getName()); + + @Value("${credentials.project-id: }") String projectId; - @Value("${credentials.key-id}") + @Value("${credentials.key-id: }") String keyId; - @Value("${credentials.key-secret}") + @Value("${credentials.key-secret: }") String keySecret; - @Value("${sms.region}") - String region; + @Value("${sms.region: }") + String smsRegion; @Bean public SinchClient sinchClient() { @@ -30,14 +33,17 @@ public SinchClient sinchClient() { if (!StringUtil.isEmpty(projectId)) { builder.setProjectId(projectId); } + if (!StringUtil.isEmpty(keyId)) { builder.setKeyId(keyId); } if (!StringUtil.isEmpty(keySecret)) { builder.setKeySecret(keySecret); } - if (!StringUtil.isEmpty(region)) { - builder.setSmsRegion(SMSRegion.from(region)); + + if (!StringUtil.isEmpty(smsRegion)) { + builder.setSmsRegion(SMSRegion.from(smsRegion)); + LOGGER.info(String.format("SMS region: '%s'", smsRegion)); } return new SinchClient(builder.build()); diff --git a/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/sms/Controller.java b/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/sms/Controller.java index 0707396..a30397e 100644 --- a/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/sms/Controller.java +++ b/getting-started/sms/respond-to-incoming-message/server/src/main/java/com/mycompany/app/sms/Controller.java @@ -22,7 +22,7 @@ public class Controller { private final SinchClient sinchClient; private final ServerBusinessLogic webhooksBusinessLogic; - @Value("${sms.webhooks.secret}") + @Value("${sms.webhooks.secret: }") private String webhooksSecret; @Autowired @@ -45,11 +45,23 @@ public ResponseEntity smsDeliveryEvent( // https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/section/Callbacks // Contact your account manager to configure your callback sending headers validation or comment // following line - var validAuth = webhooks.validateAuthenticationHeader(webhooksSecret, headers, body); + // set this value to true to validate request from Sinch servers + // see https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks for + // more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + var validAuth = + webhooks.validateAuthenticationHeader( + webhooksSecret, + // request headers + headers, + // request payload body + body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } // decode the request payload diff --git a/getting-started/sms/respond-to-incoming-message/server/src/main/resources/application.yaml b/getting-started/sms/respond-to-incoming-message/server/src/main/resources/application.yaml index 2a0ef28..06400ee 100644 --- a/getting-started/sms/respond-to-incoming-message/server/src/main/resources/application.yaml +++ b/getting-started/sms/respond-to-incoming-message/server/src/main/resources/application.yaml @@ -8,17 +8,18 @@ server: port: 8090 credentials: - # Unified related credentials, used by: - # - Numbers - # - SMS + # Required credentials for using the SMS API + # US/EU are the only supported regions with unified credentials project-id: key-id: key-secret: sms: # Sets the region for SMS - # valid values are US and EU - region: + # valid values are "us" and "eu" + # See https://github.com/sinch/sinch-sdk-java/blob/main/client/src/main/com/sinch/sdk/models/SMSRegion.java for full list of supported values but with servicePlanID + #region: + webhooks: + secret: -sms.webhooks.secret: \ No newline at end of file diff --git a/getting-started/sms/send-sms-message/client/README.md b/getting-started/sms/send-sms-message/client/README.md new file mode 100644 index 0000000..88a7d45 --- /dev/null +++ b/getting-started/sms/send-sms-message/client/README.md @@ -0,0 +1,5 @@ +# Sinch Getting started + +Code is related to [Send an SMS Message with the Sinch Java SDK](https://developers.sinch.com/docs/sms/getting-started/java/send-sms-sdk). + +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/sms/send-sms-message/client/pom.xml b/getting-started/sms/send-sms-message/client/pom.xml new file mode 100644 index 0000000..af9a3b8 --- /dev/null +++ b/getting-started/sms/send-sms-message/client/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + my.company.com + sinch-java-sdk-client-application + 1.0-SNAPSHOT + jar + Sinch Java SDK Client Application + + + [1.5.0,) + 8 + 8 + 3.13.0 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-sources + generate-sources + + add-source + + + + src/main/java + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + Application + + + + + jar-with-dependencies + + + + + package + + single + + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + org.slf4j + slf4j-jdk14 + 2.0.9 + + + + + diff --git a/getting-started/sms/send-sms-message/client/src/main/java/Application.java b/getting-started/sms/send-sms-message/client/src/main/java/Application.java new file mode 100644 index 0000000..9295865 --- /dev/null +++ b/getting-started/sms/send-sms-message/client/src/main/java/Application.java @@ -0,0 +1,42 @@ +import com.sinch.sdk.SinchClient; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import sms.SmsQuickStart; + +public abstract class Application { + + private static final String LOGGING_PROPERTIES_FILE = "logging.properties"; + private static final Logger LOGGER = initializeLogger(); + + public static void main(String[] args) { + try { + + SinchClient client = SinchClientHelper.initSinchClient(); + LOGGER.info("Application initiated. SinchClient ready."); + + SmsQuickStart sms = new SmsQuickStart(client.sms().v1()); + + } catch (Exception e) { + LOGGER.severe(String.format("Application failure: %s", e.getMessage())); + } + } + + static Logger initializeLogger() { + try (InputStream logConfigInputStream = + Application.class.getClassLoader().getResourceAsStream(LOGGING_PROPERTIES_FILE)) { + + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException( + String.format("The file '%s' couldn't be loaded.", LOGGING_PROPERTIES_FILE)); + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(Application.class.getName()); + } +} diff --git a/getting-started/sms/send-sms-message/client/src/main/java/SinchClientHelper.java b/getting-started/sms/send-sms-message/client/src/main/java/SinchClientHelper.java new file mode 100644 index 0000000..3e45ad7 --- /dev/null +++ b/getting-started/sms/send-sms-message/client/src/main/java/SinchClientHelper.java @@ -0,0 +1,95 @@ +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.Optional; +import java.util.Properties; +import java.util.logging.Logger; + +public class SinchClientHelper { + + private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); + + 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 String SMS_SERVICE_PLAN_ID = "SMS_SERVICE_PLAN_ID"; + private static final String SMS_SERVICE_PLAN_TOKEN = "SMS_SERVICE_PLAN_TOKEN"; + private static final String SMS_REGION = "SMS_REGION"; + + private static final String CONFIG_FILE = "config.properties"; + + public static SinchClient initSinchClient() { + + LOGGER.info("Initializing client"); + + Configuration configuration = getConfiguration(); + + return new SinchClient(configuration); + } + + private static Configuration getConfiguration() { + + Properties properties = loadProperties(); + + Configuration.Builder builder = Configuration.builder(); + + manageUnifiedCredentials(properties, builder); + manageSmsConfiguration(properties, builder); + + return builder.build(); + } + + private static Properties loadProperties() { + + Properties properties = new Properties(); + + try (InputStream input = + SinchClientHelper.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input != null) { + properties.load(input); + } else { + LOGGER.severe(String.format("'%s' file could not be loaded", CONFIG_FILE)); + } + } catch (IOException e) { + LOGGER.severe(String.format("Error loading properties from '%s'", CONFIG_FILE)); + } + + return properties; + } + + static void manageUnifiedCredentials(Properties properties, Configuration.Builder builder) { + + Optional projectId = getConfigValue(properties, SINCH_PROJECT_ID); + Optional keyId = getConfigValue(properties, SINCH_KEY_ID); + Optional keySecret = getConfigValue(properties, SINCH_KEY_SECRET); + + projectId.ifPresent(builder::setProjectId); + keyId.ifPresent(builder::setKeyId); + keySecret.ifPresent(builder::setKeySecret); + } + + private static void manageSmsConfiguration(Properties properties, Configuration.Builder builder) { + + Optional servicePlanId = getConfigValue(properties, SMS_SERVICE_PLAN_ID); + Optional servicePlanToken = getConfigValue(properties, SMS_SERVICE_PLAN_TOKEN); + Optional region = getConfigValue(properties, SMS_REGION); + + servicePlanId.ifPresent(builder::setSmsServicePlanId); + servicePlanToken.ifPresent(builder::setSmsApiToken); + region.ifPresent(value -> builder.setSmsRegion(SMSRegion.from(value))); + } + + private static Optional getConfigValue(Properties properties, String key) { + String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); + + // empty value means setting not set + if (null != value && value.trim().isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(value); + } +} diff --git a/getting-started/sms/send-sms-message/client/src/main/java/sms/SmsQuickStart.java b/getting-started/sms/send-sms-message/client/src/main/java/sms/SmsQuickStart.java new file mode 100644 index 0000000..3228327 --- /dev/null +++ b/getting-started/sms/send-sms-message/client/src/main/java/sms/SmsQuickStart.java @@ -0,0 +1,15 @@ +package sms; + +import com.sinch.sdk.domains.sms.api.v1.SMSService; + +public class SmsQuickStart { + + private final SMSService smsService; + + public SmsQuickStart(SMSService smsService) { + this.smsService = smsService; + + // replace by your code and business logic + Snippet.execute(this.smsService); + } +} diff --git a/getting-started/sms/send-sms-message/client/src/main/java/sms/Snippet.java b/getting-started/sms/send-sms-message/client/src/main/java/sms/Snippet.java new file mode 100644 index 0000000..37398eb --- /dev/null +++ b/getting-started/sms/send-sms-message/client/src/main/java/sms/Snippet.java @@ -0,0 +1,34 @@ +package sms; + +import com.sinch.sdk.domains.sms.api.v1.BatchesService; +import com.sinch.sdk.domains.sms.api.v1.SMSService; +import com.sinch.sdk.domains.sms.models.v1.batches.request.TextRequest; +import com.sinch.sdk.domains.sms.models.v1.batches.response.BatchResponse; +import java.util.Collections; +import java.util.logging.Logger; + +public class Snippet { + + private static final Logger LOGGER = Logger.getLogger(Snippet.class.getName()); + + static void execute(SMSService smsService) { + + BatchesService batchesService = smsService.batches(); + + String from = "YOUR_sinch_phone_number"; + String recipient = "YOUR_recipient_phone_number"; + String body = "This is a test SMS message using the Sinch Java SDK."; + + LOGGER.info(String.format("Submitting batch to send SMS to '%s'", recipient)); + + BatchResponse value = + batchesService.send( + TextRequest.builder() + .setTo(Collections.singletonList(recipient)) + .setBody(body) + .setFrom(from) + .build()); + + LOGGER.info("Response: " + value); + } +} diff --git a/getting-started/sms/send-sms-message/client/src/main/resources/config.properties b/getting-started/sms/send-sms-message/client/src/main/resources/config.properties new file mode 100644 index 0000000..735ee84 --- /dev/null +++ b/getting-started/sms/send-sms-message/client/src/main/resources/config.properties @@ -0,0 +1,12 @@ +# Required credentials for using the SMS API +SINCH_PROJECT_ID= +SINCH_KEY_ID= +SINCH_KEY_SECRET= + +# Service related configuration +#SMS_REGION = us + +# SMS Service Plan ID related credentials +# if set, these credentials will be used and enable to use regions different of US/EU +#SMS_SERVICE_PLAN_ID = +#SMS_SERVICE_PLAN_TOKEN = diff --git a/getting-started/sms/send-sms-message/client/src/main/resources/logging.properties b/getting-started/sms/send-sms-message/client/src/main/resources/logging.properties new file mode 100644 index 0000000..5ed611e --- /dev/null +++ b/getting-started/sms/send-sms-message/client/src/main/resources/logging.properties @@ -0,0 +1,8 @@ + +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = INFO +com.sinch.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/getting-started/verification/user-verification-using-sms-pin/README.md b/getting-started/verification/user-verification-using-sms-pin/README.md index 3d72656..04167ce 100644 --- a/getting-started/verification/user-verification-using-sms-pin/README.md +++ b/getting-started/verification/user-verification-using-sms-pin/README.md @@ -2,4 +2,4 @@ Code is related to [Verify a user using SMS PIN with the Java SDK](https://developers.sinch.com/docs/verification/getting-started/java-sdk/sms-verification/). -See [Server template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/server/README.md) for details about configuration and usage. +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/verification/user-verification-using-sms-pin/client/src/main/java/SinchClientHelper.java b/getting-started/verification/user-verification-using-sms-pin/client/src/main/java/SinchClientHelper.java index 49ed29b..4b78d21 100644 --- a/getting-started/verification/user-verification-using-sms-pin/client/src/main/java/SinchClientHelper.java +++ b/getting-started/verification/user-verification-using-sms-pin/client/src/main/java/SinchClientHelper.java @@ -1,6 +1,5 @@ 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.Optional; @@ -11,17 +10,9 @@ public class SinchClientHelper { private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); - 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 String APPLICATION_API_KEY = "APPLICATION_API_KEY"; private static final String APPLICATION_API_SECRET = "APPLICATION_API_SECRET"; - private static final String SMS_SERVICE_PLAN_ID = "SMS_SERVICE_PLAN_ID"; - private static final String SMS_SERVICE_PLAN_TOKEN = "SMS_SERVICE_PLAN_TOKEN"; - private static final String SMS_REGION = "SMS_REGION"; - private static final String CONFIG_FILE = "config.properties"; public static SinchClient initSinchClient() { @@ -39,9 +30,7 @@ private static Configuration getConfiguration() { Configuration.Builder builder = Configuration.builder(); - manageUnifiedCredentials(properties, builder); manageApplicationCredentials(properties, builder); - manageSmsConfiguration(properties, builder); return builder.build(); } @@ -64,17 +53,6 @@ private static Properties loadProperties() { return properties; } - static void manageUnifiedCredentials(Properties properties, Configuration.Builder builder) { - - Optional projectId = getConfigValue(properties, SINCH_PROJECT_ID); - Optional keyId = getConfigValue(properties, SINCH_KEY_ID); - Optional keySecret = getConfigValue(properties, SINCH_KEY_SECRET); - - projectId.ifPresent(builder::setProjectId); - keyId.ifPresent(builder::setKeyId); - keySecret.ifPresent(builder::setKeySecret); - } - private static void manageApplicationCredentials( Properties properties, Configuration.Builder builder) { @@ -85,17 +63,6 @@ private static void manageApplicationCredentials( verificationApiSecret.ifPresent(builder::setApplicationSecret); } - private static void manageSmsConfiguration(Properties properties, Configuration.Builder builder) { - - Optional servicePlanId = getConfigValue(properties, SMS_SERVICE_PLAN_ID); - Optional servicePlanToken = getConfigValue(properties, SMS_SERVICE_PLAN_TOKEN); - Optional region = getConfigValue(properties, SMS_REGION); - - servicePlanId.ifPresent(builder::setSmsServicePlanId); - servicePlanToken.ifPresent(builder::setSmsApiToken); - region.ifPresent(value -> builder.setSmsRegion(SMSRegion.from(value))); - } - private static Optional getConfigValue(Properties properties, String key) { String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); diff --git a/getting-started/verification/user-verification-using-sms-pin/client/src/main/resources/config.properties b/getting-started/verification/user-verification-using-sms-pin/client/src/main/resources/config.properties index 037d872..ff60106 100644 --- a/getting-started/verification/user-verification-using-sms-pin/client/src/main/resources/config.properties +++ b/getting-started/verification/user-verification-using-sms-pin/client/src/main/resources/config.properties @@ -1,2 +1,3 @@ +# Required credentials for using the Verification API APPLICATION_API_KEY= APPLICATION_API_SECRET= diff --git a/getting-started/voice/make-a-call/client/README.md b/getting-started/voice/make-a-call/client/README.md new file mode 100644 index 0000000..ca38b66 --- /dev/null +++ b/getting-started/voice/make-a-call/client/README.md @@ -0,0 +1,5 @@ +# Sinch Getting started + +Code is related to [Make a call with Java SDK](https://developers.sinch.com/docs/voice/getting-started/java-sdk/make-call/#make-a-call-with-java-sdk). + +See [Client template README](https://github.com/sinch/sinch-sdk-java-quickstart/blob/main/templates/client/README.md) for details about configuration and usage. diff --git a/getting-started/voice/make-a-call/client/pom.xml b/getting-started/voice/make-a-call/client/pom.xml new file mode 100644 index 0000000..823a8e2 --- /dev/null +++ b/getting-started/voice/make-a-call/client/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + my.company.com + sinch-java-sdk-client-application + 1.0-SNAPSHOT + jar + Sinch Java SDK Client Application + + + [1.5.0,) + 8 + 8 + 3.13.0 + UTF-8 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + add-sources + generate-sources + + add-source + + + + src/main/java + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + Application + + + + + jar-with-dependencies + + + + + package + + single + + + + + + + + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + org.slf4j + slf4j-jdk14 + 2.0.9 + + + + + diff --git a/getting-started/voice/make-a-call/client/src/main/java/Application.java b/getting-started/voice/make-a-call/client/src/main/java/Application.java new file mode 100644 index 0000000..de21eb8 --- /dev/null +++ b/getting-started/voice/make-a-call/client/src/main/java/Application.java @@ -0,0 +1,42 @@ +import com.sinch.sdk.SinchClient; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import voice.VoiceQuickStart; + +public abstract class Application { + + private static final String LOGGING_PROPERTIES_FILE = "logging.properties"; + private static final Logger LOGGER = initializeLogger(); + + public static void main(String[] args) { + try { + + SinchClient client = SinchClientHelper.initSinchClient(); + LOGGER.info("Application initiated. SinchClient ready."); + + VoiceQuickStart voice = new VoiceQuickStart(client.voice().v1()); + + } catch (Exception e) { + LOGGER.severe(String.format("Application failure: %s", e.getMessage())); + } + } + + static Logger initializeLogger() { + try (InputStream logConfigInputStream = + Application.class.getClassLoader().getResourceAsStream(LOGGING_PROPERTIES_FILE)) { + + if (logConfigInputStream != null) { + LogManager.getLogManager().readConfiguration(logConfigInputStream); + } else { + throw new RuntimeException( + String.format("The file '%s' couldn't be loaded.", LOGGING_PROPERTIES_FILE)); + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + return Logger.getLogger(Application.class.getName()); + } +} diff --git a/getting-started/voice/make-a-call/client/src/main/java/SinchClientHelper.java b/getting-started/voice/make-a-call/client/src/main/java/SinchClientHelper.java new file mode 100644 index 0000000..4b78d21 --- /dev/null +++ b/getting-started/voice/make-a-call/client/src/main/java/SinchClientHelper.java @@ -0,0 +1,76 @@ +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.models.Configuration; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; +import java.util.Properties; +import java.util.logging.Logger; + +public class SinchClientHelper { + + private static final Logger LOGGER = Logger.getLogger(SinchClientHelper.class.getName()); + + private static final String APPLICATION_API_KEY = "APPLICATION_API_KEY"; + private static final String APPLICATION_API_SECRET = "APPLICATION_API_SECRET"; + + private static final String CONFIG_FILE = "config.properties"; + + public static SinchClient initSinchClient() { + + LOGGER.info("Initializing client"); + + Configuration configuration = getConfiguration(); + + return new SinchClient(configuration); + } + + private static Configuration getConfiguration() { + + Properties properties = loadProperties(); + + Configuration.Builder builder = Configuration.builder(); + + manageApplicationCredentials(properties, builder); + + return builder.build(); + } + + private static Properties loadProperties() { + + Properties properties = new Properties(); + + try (InputStream input = + SinchClientHelper.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) { + if (input != null) { + properties.load(input); + } else { + LOGGER.severe(String.format("'%s' file could not be loaded", CONFIG_FILE)); + } + } catch (IOException e) { + LOGGER.severe(String.format("Error loading properties from '%s'", CONFIG_FILE)); + } + + return properties; + } + + private static void manageApplicationCredentials( + Properties properties, Configuration.Builder builder) { + + Optional verificationApiKey = getConfigValue(properties, APPLICATION_API_KEY); + Optional verificationApiSecret = getConfigValue(properties, APPLICATION_API_SECRET); + + verificationApiKey.ifPresent(builder::setApplicationKey); + verificationApiSecret.ifPresent(builder::setApplicationSecret); + } + + private static Optional getConfigValue(Properties properties, String key) { + String value = null != System.getenv(key) ? System.getenv(key) : properties.getProperty(key); + + // empty value means setting not set + if (null != value && value.trim().isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable(value); + } +} diff --git a/getting-started/voice/make-a-call/client/src/main/java/voice/Snippet.java b/getting-started/voice/make-a-call/client/src/main/java/voice/Snippet.java new file mode 100644 index 0000000..4b844ca --- /dev/null +++ b/getting-started/voice/make-a-call/client/src/main/java/voice/Snippet.java @@ -0,0 +1,34 @@ +package voice; + +import com.sinch.sdk.domains.voice.api.v1.CalloutsService; +import com.sinch.sdk.domains.voice.api.v1.VoiceService; +import com.sinch.sdk.domains.voice.models.v1.callouts.request.CalloutRequestTTS; +import com.sinch.sdk.domains.voice.models.v1.destination.DestinationPstn; +import java.util.logging.Logger; + +public class Snippet { + + private static final Logger LOGGER = Logger.getLogger(Snippet.class.getName()); + + public static String execute(VoiceService voiceService) { + + CalloutsService calloutsService = voiceService.callouts(); + + String phoneNumber = "YOUR_phone_number"; + String message = "Hello, this is a call from Sinch. Congratulations! You made your first call."; + + LOGGER.info("Calling '" + phoneNumber + '"'); + + CalloutRequestTTS parameters = + CalloutRequestTTS.builder() + .setDestination(DestinationPstn.from(phoneNumber)) + .setText(message) + .build(); + + String callId = calloutsService.textToSpeech(parameters); + + LOGGER.info("Call started with id: '" + callId + '"'); + + return callId; + } +} diff --git a/getting-started/voice/make-a-call/client/src/main/java/voice/VoiceQuickStart.java b/getting-started/voice/make-a-call/client/src/main/java/voice/VoiceQuickStart.java new file mode 100644 index 0000000..a7ecc89 --- /dev/null +++ b/getting-started/voice/make-a-call/client/src/main/java/voice/VoiceQuickStart.java @@ -0,0 +1,15 @@ +package voice; + +import com.sinch.sdk.domains.voice.api.v1.VoiceService; + +public class VoiceQuickStart { + + private final VoiceService voiceService; + + public VoiceQuickStart(VoiceService voiceService) { + this.voiceService = voiceService; + + // replace by your code and business logic + Snippet.execute(this.voiceService); + } +} diff --git a/getting-started/voice/make-a-call/client/src/main/resources/config.properties b/getting-started/voice/make-a-call/client/src/main/resources/config.properties new file mode 100644 index 0000000..9e32513 --- /dev/null +++ b/getting-started/voice/make-a-call/client/src/main/resources/config.properties @@ -0,0 +1,3 @@ +# Required credentials for using the Voice API +APPLICATION_API_KEY= +APPLICATION_API_SECRET= diff --git a/getting-started/voice/make-a-call/client/src/main/resources/logging.properties b/getting-started/voice/make-a-call/client/src/main/resources/logging.properties new file mode 100644 index 0000000..5ed611e --- /dev/null +++ b/getting-started/voice/make-a-call/client/src/main/resources/logging.properties @@ -0,0 +1,8 @@ + +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = INFO +com.sinch.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/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/Config.java b/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/Config.java index 23b3ace..424ef34 100644 --- a/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/Config.java +++ b/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/Config.java @@ -3,16 +3,19 @@ import com.sinch.sdk.SinchClient; import com.sinch.sdk.core.utils.StringUtil; import com.sinch.sdk.models.Configuration; +import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @org.springframework.context.annotation.Configuration public class Config { - @Value("${credentials.application-api-key}") + private static final Logger LOGGER = Logger.getLogger(Config.class.getName()); + + @Value("${credentials.application-api-key: }") String applicationKey; - @Value("${credentials.application-api-secret}") + @Value("${credentials.application-api-secret: }") String applicationSecret; @Bean diff --git a/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/Controller.java b/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/Controller.java index 7f97aab..e56f348 100644 --- a/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/Controller.java +++ b/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/Controller.java @@ -1,9 +1,9 @@ package com.mycompany.app.voice; import com.sinch.sdk.SinchClient; -import com.sinch.sdk.domains.voice.WebHooksService; -import com.sinch.sdk.domains.voice.models.webhooks.DisconnectCallEvent; -import com.sinch.sdk.domains.voice.models.webhooks.IncomingCallEvent; +import com.sinch.sdk.domains.voice.api.v1.WebHooksService; +import com.sinch.sdk.domains.voice.models.v1.webhooks.DisconnectedCallEvent; +import com.sinch.sdk.domains.voice.models.v1.webhooks.IncomingCallEvent; import java.util.Map; import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -36,33 +36,41 @@ public Controller(SinchClient sinchClient, ServerBusinessLogic webhooksBusinessL public ResponseEntity VoiceEvent( @RequestHeader Map headers, @RequestBody String body) { - WebHooksService webhooks = sinchClient.voice().webhooks(); + WebHooksService webhooks = sinchClient.voice().v1().webhooks(); // ensure valid authentication to handle request - var validAuth = - webhooks.validateAuthenticatedRequest( - // The HTTP verb this controller is managing - "POST", - // The URI this controller is managing - "/VoiceEvent", - // request headers - headers, - // request payload body - body); + // set this value to true to validate request from Sinch servers + // see + // https://developers.sinch.com/docs/voice/api-reference/authentication/callback-signed-request + // for more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + // ensure valid authentication to handle request + var validAuth = + webhooks.validateAuthenticationHeader( + // The HTTP verb this controller is managing + "POST", + // The URI this controller is managing + "/VoiceEvent", + // request headers + headers, + // request payload body + body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } // decode the payload request - var event = webhooks.unserializeWebhooksEvent(body); + var event = webhooks.parseEvent(body); // let business layer process the request var response = switch (event) { case IncomingCallEvent e -> webhooksBusinessLogic.incoming(e); - case DisconnectCallEvent e -> { + case DisconnectedCallEvent e -> { webhooksBusinessLogic.disconnect(e); yield null; } @@ -71,7 +79,7 @@ public ResponseEntity VoiceEvent( String serializedResponse = ""; if (null != response) { - serializedResponse = webhooks.serializeWebhooksResponse(response); + serializedResponse = webhooks.serializeResponse(response); } LOGGER.finest("JSON response: " + serializedResponse); diff --git a/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/ServerBusinessLogic.java b/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/ServerBusinessLogic.java index b4eb7b9..c339699 100644 --- a/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/ServerBusinessLogic.java +++ b/getting-started/voice/respond-to-incoming-call/server/src/main/java/com/mycompany/app/voice/ServerBusinessLogic.java @@ -1,10 +1,10 @@ package com.mycompany.app.voice; -import com.sinch.sdk.domains.voice.models.svaml.ActionHangUp; -import com.sinch.sdk.domains.voice.models.svaml.InstructionSay; -import com.sinch.sdk.domains.voice.models.svaml.SVAMLControl; -import com.sinch.sdk.domains.voice.models.webhooks.DisconnectCallEvent; -import com.sinch.sdk.domains.voice.models.webhooks.IncomingCallEvent; +import com.sinch.sdk.domains.voice.models.v1.svaml.SvamlControl; +import com.sinch.sdk.domains.voice.models.v1.svaml.action.SvamlActionHangup; +import com.sinch.sdk.domains.voice.models.v1.svaml.instruction.SvamlInstructionSay; +import com.sinch.sdk.domains.voice.models.v1.webhooks.DisconnectedCallEvent; +import com.sinch.sdk.domains.voice.models.v1.webhooks.IncomingCallEvent; import java.util.Collections; import java.util.logging.Logger; import org.springframework.stereotype.Component; @@ -14,21 +14,21 @@ public class ServerBusinessLogic { private static final Logger LOGGER = Logger.getLogger(ServerBusinessLogic.class.getName()); - public SVAMLControl incoming(IncomingCallEvent event) { + public SvamlControl incoming(IncomingCallEvent event) { LOGGER.info("Handle event: " + event); String instruction = "Thank you for calling your Sinch number. You've just handled an incoming call."; - return SVAMLControl.builder() - .setAction(ActionHangUp.builder().build()) + return SvamlControl.builder() + .setAction(SvamlActionHangup.builder().build()) .setInstructions( - Collections.singletonList(InstructionSay.builder().setText(instruction).build())) + Collections.singletonList(SvamlInstructionSay.builder().setText(instruction).build())) .build(); } - public void disconnect(DisconnectCallEvent event) { + public void disconnect(DisconnectedCallEvent event) { LOGGER.info("Handle event: " + event); } diff --git a/getting-started/voice/respond-to-incoming-call/server/src/main/resources/application.yaml b/getting-started/voice/respond-to-incoming-call/server/src/main/resources/application.yaml index 5d82fb2..f8d0e21 100644 --- a/getting-started/voice/respond-to-incoming-call/server/src/main/resources/application.yaml +++ b/getting-started/voice/respond-to-incoming-call/server/src/main/resources/application.yaml @@ -8,8 +8,6 @@ server: port: 8090 credentials: - # Application related credentials, used by: - # - Verification - # - Voice + # Required credentials for using the Voice API application-api-key: application-api-secret: diff --git a/templates/client/pom.xml b/templates/client/pom.xml index af779f8..af9a3b8 100644 --- a/templates/client/pom.xml +++ b/templates/client/pom.xml @@ -74,11 +74,9 @@ ${sinch.sdk.java.version} - org.slf4j - slf4j-nop + slf4j-jdk14 2.0.9 diff --git a/templates/client/src/main/java/SinchClientHelper.java b/templates/client/src/main/java/SinchClientHelper.java index 49ed29b..9e1b6a8 100644 --- a/templates/client/src/main/java/SinchClientHelper.java +++ b/templates/client/src/main/java/SinchClientHelper.java @@ -1,5 +1,6 @@ import com.sinch.sdk.SinchClient; import com.sinch.sdk.models.Configuration; +import com.sinch.sdk.models.ConversationRegion; import com.sinch.sdk.models.SMSRegion; import java.io.IOException; import java.io.InputStream; @@ -22,6 +23,8 @@ public class SinchClientHelper { private static final String SMS_SERVICE_PLAN_TOKEN = "SMS_SERVICE_PLAN_TOKEN"; private static final String SMS_REGION = "SMS_REGION"; + private static final String CONVERSATION_REGION = "CONVERSATION_REGION"; + private static final String CONFIG_FILE = "config.properties"; public static SinchClient initSinchClient() { @@ -41,6 +44,7 @@ private static Configuration getConfiguration() { manageUnifiedCredentials(properties, builder); manageApplicationCredentials(properties, builder); + manageConversationConfiguration(properties, builder); manageSmsConfiguration(properties, builder); return builder.build(); @@ -85,6 +89,14 @@ private static void manageApplicationCredentials( verificationApiSecret.ifPresent(builder::setApplicationSecret); } + private static void manageConversationConfiguration( + Properties properties, Configuration.Builder builder) { + + Optional region = getConfigValue(properties, CONVERSATION_REGION); + + region.ifPresent(value -> builder.setConversationRegion(ConversationRegion.from(value))); + } + private static void manageSmsConfiguration(Properties properties, Configuration.Builder builder) { Optional servicePlanId = getConfigValue(properties, SMS_SERVICE_PLAN_ID); diff --git a/templates/client/src/main/resources/config.properties b/templates/client/src/main/resources/config.properties index f18db35..50c967f 100644 --- a/templates/client/src/main/resources/config.properties +++ b/templates/client/src/main/resources/config.properties @@ -1,6 +1,7 @@ -# Unified related credentials, used by: +# Java SDK domains supporting unified credentials # - Numbers # - SMS: US/EU are the only supported regions with unified credentials +# - Conversation SINCH_PROJECT_ID = SINCH_KEY_ID = SINCH_KEY_SECRET = @@ -11,9 +12,14 @@ SINCH_KEY_SECRET = #APPLICATION_API_KEY = #APPLICATION_API_SECRET = -# Service related configuration -SMS_REGION = us # SMS Service Plan ID related credentials # if set, these credentials will be used and enable to use regions different of US/EU #SMS_SERVICE_PLAN_ID = #SMS_SERVICE_PLAN_TOKEN = + +# See https://github.com/sinch/sinch-sdk-java/blob/main/client/src/main/com/sinch/sdk/models/SMSRegion.java for full list of supported values but with servicePlanID +# valid values are "us" and "eu" +#SMS_REGION = us + +# See https://github.com/sinch/sinch-sdk-java/blob/main/client/src/main/com/sinch/sdk/models/ConversationRegion.java for list of supported values +#CONVERSATION_REGION = us diff --git a/templates/server/src/main/java/com/mycompany/app/Config.java b/templates/server/src/main/java/com/mycompany/app/Config.java index 4cbc290..427dfb3 100644 --- a/templates/server/src/main/java/com/mycompany/app/Config.java +++ b/templates/server/src/main/java/com/mycompany/app/Config.java @@ -3,30 +3,37 @@ import com.sinch.sdk.SinchClient; import com.sinch.sdk.core.utils.StringUtil; import com.sinch.sdk.models.Configuration; +import com.sinch.sdk.models.ConversationRegion; import com.sinch.sdk.models.SMSRegion; +import java.util.logging.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @org.springframework.context.annotation.Configuration public class Config { - @Value("${credentials.project-id}") + private static final Logger LOGGER = Logger.getLogger(Config.class.getName()); + + @Value("${credentials.project-id: }") String projectId; - @Value("${credentials.key-id}") + @Value("${credentials.key-id: }") String keyId; - @Value("${credentials.key-secret}") + @Value("${credentials.key-secret: }") String keySecret; - @Value("${credentials.application-api-key}") + @Value("${credentials.application-api-key: }") String applicationKey; - @Value("${credentials.application-api-secret}") + @Value("${credentials.application-api-secret: }") String applicationSecret; - @Value("${sms.region}") - String region; + @Value("${conversation.region: }") + String conversationRegion; + + @Value("${sms.region: }") + String smsRegion; @Bean public SinchClient sinchClient() { @@ -51,8 +58,15 @@ public SinchClient sinchClient() { if (!StringUtil.isEmpty(applicationSecret)) { builder.setApplicationSecret(applicationSecret); } - if (!StringUtil.isEmpty(region)) { - builder.setSmsRegion(SMSRegion.from(region)); + + if (!StringUtil.isEmpty(conversationRegion)) { + builder.setConversationRegion(ConversationRegion.from(conversationRegion)); + LOGGER.info(String.format("Conversation region: '%s'", conversationRegion)); + } + + if (!StringUtil.isEmpty(smsRegion)) { + builder.setSmsRegion(SMSRegion.from(smsRegion)); + LOGGER.info(String.format("SMS region: '%s'", smsRegion)); } return new SinchClient(builder.build()); diff --git a/templates/server/src/main/java/com/mycompany/app/conversation/Controller.java b/templates/server/src/main/java/com/mycompany/app/conversation/Controller.java index e10497f..befb083 100644 --- a/templates/server/src/main/java/com/mycompany/app/conversation/Controller.java +++ b/templates/server/src/main/java/com/mycompany/app/conversation/Controller.java @@ -42,7 +42,7 @@ public class Controller { private final SinchClient sinchClient; private final ServerBusinessLogic webhooksBusinessLogic; - @Value("${conversation.webhooks.secret}") + @Value("${conversation.webhooks.secret: }") private String webhooksSecret; @Autowired @@ -60,12 +60,18 @@ public ResponseEntity ConversationEvent( WebHooksService webhooks = sinchClient.conversation().v1().webhooks(); - // ensure valid authentication to handle request - var validAuth = webhooks.validateAuthenticationHeader(webhooksSecret, headers, body); + // set this value to true to validate request from Sinch servers + // see https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks for + // more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + // ensure valid authentication to handle request + var validAuth = webhooks.validateAuthenticationHeader(webhooksSecret, headers, body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } // decode the request payload diff --git a/templates/server/src/main/java/com/mycompany/app/numbers/Controller.java b/templates/server/src/main/java/com/mycompany/app/numbers/Controller.java index 6d923c0..c6909e9 100644 --- a/templates/server/src/main/java/com/mycompany/app/numbers/Controller.java +++ b/templates/server/src/main/java/com/mycompany/app/numbers/Controller.java @@ -29,7 +29,7 @@ public class Controller { * href="https://developers.sinch.com/java-sdk/apidocs/com/sinch/sdk/domains/numbers/api/v1/CallbackConfigurationService.html#update(com.sinch.sdk.domains.numbers.models.v1.callbacks.request.CallbackConfigurationUpdateRequest)">update * function Javadoc */ - @Value("${numbers.webhooks.secret}") + @Value("${numbers.webhooks.secret: }") private String webhooksSecret; @Autowired @@ -47,18 +47,24 @@ public ResponseEntity NumbersEvent( WebHooksService webhooks = sinchClient.numbers().v1().webhooks(); - // ensure valid authentication to handle request - var validAuth = - webhooks.validateAuthenticationHeader( - webhooksSecret, - // request headers - headers, - // request payload body - body); + // set this value to true to validate request from Sinch servers + // see https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks for + // more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + // ensure valid authentication to handle request + var validAuth = + webhooks.validateAuthenticationHeader( + webhooksSecret, + // request headers + headers, + // request payload body + body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } // decode the request payload diff --git a/templates/server/src/main/java/com/mycompany/app/sms/Controller.java b/templates/server/src/main/java/com/mycompany/app/sms/Controller.java index 4041044..a7f75d1 100644 --- a/templates/server/src/main/java/com/mycompany/app/sms/Controller.java +++ b/templates/server/src/main/java/com/mycompany/app/sms/Controller.java @@ -23,7 +23,7 @@ public class Controller { private final SinchClient sinchClient; private final ServerBusinessLogic webhooksBusinessLogic; - @Value("${sms.webhooks.secret}") + @Value("${sms.webhooks.secret: }") private String webhooksSecret; @Autowired @@ -46,11 +46,23 @@ public ResponseEntity smsDeliveryEvent( // https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/section/Callbacks // Contact your account manager to configure your callback sending headers validation or comment // following line - var validAuth = webhooks.validateAuthenticationHeader(webhooksSecret, headers, body); + // set this value to true to validate request from Sinch servers + // see https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks for + // more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + var validAuth = + webhooks.validateAuthenticationHeader( + webhooksSecret, + // request headers + headers, + // request payload body + body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } // decode the request payload diff --git a/templates/server/src/main/java/com/mycompany/app/verification/Controller.java b/templates/server/src/main/java/com/mycompany/app/verification/Controller.java index ccaa633..0884904 100644 --- a/templates/server/src/main/java/com/mycompany/app/verification/Controller.java +++ b/templates/server/src/main/java/com/mycompany/app/verification/Controller.java @@ -39,20 +39,27 @@ public ResponseEntity VerificationEvent( WebHooksService webhooks = sinchClient.verification().v1().webhooks(); // ensure valid authentication to handle request - var validAuth = - webhooks.validateAuthenticationHeader( - // The HTTP verb this controller is managing - "POST", - // The URI this controller is managing - "/VerificationEvent", - // request headers - headers, - // request payload body - body); + // set this value to true to validate request from Sinch servers + // see + // https://developers.sinch.com/docs/verification/api-reference/authentication/callback-signed-request + // for more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + var validAuth = + webhooks.validateAuthenticationHeader( + // The HTTP verb this controller is managing + "POST", + // The URI this controller is managing + "/VerificationEvent", + // request headers + headers, + // request payload body + body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } // decode the request payload diff --git a/templates/server/src/main/java/com/mycompany/app/voice/Controller.java b/templates/server/src/main/java/com/mycompany/app/voice/Controller.java index d109765..3332507 100644 --- a/templates/server/src/main/java/com/mycompany/app/voice/Controller.java +++ b/templates/server/src/main/java/com/mycompany/app/voice/Controller.java @@ -42,22 +42,29 @@ public ResponseEntity VoiceEvent( WebHooksService webhooks = sinchClient.voice().v1().webhooks(); // ensure valid authentication to handle request - var validAuth = - webhooks.validateAuthenticationHeader( - // The HTTP verb this controller is managing - "POST", - // The URI this controller is managing - "/VoiceEvent", - // request headers - headers, - // request payload body - body); + // set this value to true to validate request from Sinch servers + // see + // https://developers.sinch.com/docs/voice/api-reference/authentication/callback-signed-request + // for more information + boolean ensureValidAuthentication = false; + if (ensureValidAuthentication) { + // ensure valid authentication to handle request + var validAuth = + webhooks.validateAuthenticationHeader( + // The HTTP verb this controller is managing + "POST", + // The URI this controller is managing + "/VoiceEvent", + // request headers + headers, + // request payload body + body); - // token validation failed - if (!validAuth) { - throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } } - // decode the payload request var event = webhooks.parseEvent(body); diff --git a/templates/server/src/main/resources/application.yaml b/templates/server/src/main/resources/application.yaml index 3db4ba9..c94f5ee 100644 --- a/templates/server/src/main/resources/application.yaml +++ b/templates/server/src/main/resources/application.yaml @@ -8,9 +8,10 @@ server: port: 8090 credentials: - # Unified related credentials, used by: + # Java SDK domains supporting unified credentials # - Numbers - # - SMS + # - SMS: US/EU are the only supported regions with unified credentials + # - Conversation project-id: key-id: key-secret: @@ -19,19 +20,26 @@ credentials: # - Verification # - Voice application-api-key: - application-api-secret: - -sms: - # Sets the region for SMS - # valid values are US and EU - region: + application-api-secret: # Secret value set for Numbers webhooks calls validation -numbers.webhooks.secret: +numbers: + webhooks: + secret: # Secret value set for Conversation webhooks calls validation -conversation.webhooks.secret: +conversation: + # Sets the region for Conversation + # See https://github.com/sinch/sinch-sdk-java/blob/main/client/src/main/com/sinch/sdk/models/ConversationRegion.java for list of supported values + region: + webhooks: + secret: + +sms: + # Sets the region for SMS + # valid values are "us" and "eu" + # See https://github.com/sinch/sinch-sdk-java/blob/main/client/src/main/com/sinch/sdk/models/SMSRegion.java for full list of supported values but with servicePlanID + region: + webhooks: + secret: -# Secret value set for Conversation webhooks calls validation -sms.webhooks.secret: - \ No newline at end of file