From 3cde2f2dbd8cf05be8a487428de326b477db5414 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier Date: Tue, 23 Apr 2024 16:09:11 +0200 Subject: [PATCH 1/7] docs (Voice): Add 'handle incoming call' tutorial --- examples/compile.sh | 1 + .../voice/handle-incoming-call/README.md | 76 +++++++++++++++++++ .../voice/handle-incoming-call/pom.xml | 52 +++++++++++++ .../src/main/java/com/mycompany/app/App.java | 12 +++ .../main/java/com/mycompany/app/Config.java | 37 +++++++++ .../com/mycompany/app/WebhookController.java | 67 ++++++++++++++++ .../com/mycompany/app/WebhookService.java | 33 ++++++++ .../src/main/resources/application.yaml | 16 ++++ 8 files changed, 294 insertions(+) create mode 100644 examples/tutorials/voice/handle-incoming-call/README.md create mode 100644 examples/tutorials/voice/handle-incoming-call/pom.xml create mode 100644 examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/App.java create mode 100644 examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java create mode 100644 examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookController.java create mode 100644 examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookService.java create mode 100644 examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml diff --git a/examples/compile.sh b/examples/compile.sh index d527bf73..74b622fd 100755 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -1,3 +1,4 @@ #!/bin/sh (cd tutorials/sms/auto-subscribe-app && mvn clean package) +(cd tutorials/voice/handle-incoming-call && mvn clean package) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md new file mode 100644 index 00000000..52fc66f8 --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -0,0 +1,76 @@ +# Handle Inconing Call webhook application sample + +This directory contains sample related to Java SDK tutorial: [Handle incoming call](https://developers.sinch.com/docs/voice/getting-started/java/incoming-call) + +## Requirements + +- JDK 21 or later +- [Maven](https://maven.apache.org/) +- [ngrok](https://ngrok.com/docs) +- [Sinch account](https://dashboard.sinch.com) + +## Usage + +### Configure application settings + +Application settings is using the SpringBoot configuration file: [`application.yaml`](src/main/resources/application.yaml) file and set: + +#### Sinch credentials +Located in `credentials` section (*you can find Voice application credentials you need on your [Sinch dashboard](https://dashboard.sinch.com/voice/apps)*): +- `api-id`: YOUR_Voice_Application_Id +- `api-secret`: YOUR_Voice_Application_Secret + +#### Webhook controller path +Located in `voice` section you can modify: +- `controller-path`: path the server will respond to. Default: /voice + +#### Server port +Located in `server` section: +- port: The port to be used to listen incoming request. Default: 8090 + +### Starting server locally + +Compile and run the application as server onto you localhost. +```bash +mvn spring-boot:run +``` + +### Use ngrok to forward request to local server + +Forwarding request to same `8090` port used above: + +*Note: The `8090` value is coming from default config and can be changed (see [Server port](#Server port) configuration section)* + +```bash +ngrok http 8090 +``` + +ngrok output will contains output like: +``` +ngrok (Ctrl+C to quit) + +... +Forwarding https://0e64-78-117-86-140.ngrok-free.app -> http://localhost:8090 + +``` +The line +``` +Forwarding https://0e64-78-117-86-140.ngrok-free.app -> http://localhost:8090 +``` +Contains `https://0e64-78-117-86-140.ngrok-free.app` value. + +## Configure Voice application callback + +You have now a local web server responding to `/voice` path and `ngrok` instance running providing a bridge from `https://0e64-78-117-86-140.ngrok-free.app` onto local webserver. + +Last step is now to configure your dashboard to set Voice application callback to point your local webserver. + +The callback to be called by sinch will be in form of: ``/`` + +e.g. According to previous sample values: https://0e64-78-117-86-140.ngrok-free.app/voice + +1. Go to your Voice application configuration: [Sinch Voice Apps dashboard](https://dashboard.sinch.com/voice/apps/) +2. Edit the application setting related to the Application ID set from config file (see [Sinch credentials](#configure-application-settings)) +3. Fill `Callback URL` field with *https://0e64-78-117-86-140.ngrok-free.app/voice* + +You can now perform a to your number, the webhook will be called and serve response to callee. diff --git a/examples/tutorials/voice/handle-incoming-call/pom.xml b/examples/tutorials/voice/handle-incoming-call/pom.xml new file mode 100644 index 00000000..8a811136 --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + com.sinch.sdk + sinch-sdk-java-sample-webhooks-app + 0.0.1-SNAPSHOT + Sinch Java SDK Handle Incoming Call Sample Application + Demo Project + + + [1.0.0,) + 21 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + com.sinch.sdk + sinch-sdk-java + ${sinch.sdk.java.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/App.java b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/App.java new file mode 100644 index 00000000..195ae602 --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/App.java @@ -0,0 +1,12 @@ +package com.mycompany.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java new file mode 100644 index 00000000..ef1cd7cb --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java @@ -0,0 +1,37 @@ +package com.mycompany.app; + +import com.sinch.sdk.SinchClient; +import com.sinch.sdk.domains.voice.VoiceService; +import com.sinch.sdk.models.Configuration; +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}") + String applicationApiKey; + + @Value("${credentials.application.api-secret}") + String applicationApiSecret; + + @Value("${voice.controller-path}") + private String controllerPath; + + @Bean + public VoiceService voiceService() { + + var configuration = + Configuration.builder() + .setApplicationKey(applicationApiKey) + .setApplicationSecret(applicationApiSecret) + .build(); + + return new SinchClient(configuration).voice(); + } + + @Bean + String controllerPath() { + return controllerPath; + } +} diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookController.java b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookController.java new file mode 100644 index 00000000..55548e90 --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookController.java @@ -0,0 +1,67 @@ +package com.mycompany.app; + +import com.sinch.sdk.domains.voice.VoiceService; +import com.sinch.sdk.domains.voice.models.webhooks.IncomingCallEvent; +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +@RestController +public class WebhookController { + + private final VoiceService voiceService; + private final WebhookService webhookService; + private final String controllerPath; + + @Autowired + public WebhookController( + VoiceService voiceService, WebhookService webhookService, String controllerPath) { + this.voiceService = voiceService; + this.webhookService = webhookService; + this.controllerPath = controllerPath; + } + + @PostMapping( + value = "#{controllerPath}", + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + public String voiceEvent(@RequestHeader Map headers, @RequestBody String body) { + + // ensure valid authentication before handling request + var validAuth = + voiceService + .webhooks() + .validateAuthenticatedRequest( + // The HTTP verb this controller is managing + "POST", + // The URI this path is managing + controllerPath, + // request headers + headers, + // request payload body + body); + + // token validation failed + if (!validAuth) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } + + // decode the payload request + var event = voiceService.webhooks().unserializeWebhooksEvent(body); + + if (!(event instanceof IncomingCallEvent)) { + return ""; + } + + // let business layer process the request + var response = webhookService.answered((IncomingCallEvent) event); + + return voiceService.webhooks().serializeWebhooksResponse(response); + } +} diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookService.java b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookService.java new file mode 100644 index 00000000..17910886 --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/WebhookService.java @@ -0,0 +1,33 @@ +package com.mycompany.app; + +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.IncomingCallEvent; +import java.util.Collections; +import java.util.logging.Logger; +import org.springframework.stereotype.Component; + +@Component +public class WebhookService { + + private static final Logger LOGGER = Logger.getLogger(WebhookService.class.getName()); + + public SVAMLControl answered(IncomingCallEvent event) { + + LOGGER.info( + "Incoming call from '%s' received for '%s' :".formatted(event.getCli(), event.getTo())); + + var hangupAction = ActionHangUp.builder().build(); + + var sayInstruction = + InstructionSay.builder() + .setText("Thank you for calling Sinch. This call will now end.") + .build(); + + return SVAMLControl.builder() + .setInstructions(Collections.singletonList(sayInstruction)) + .setAction(hangupAction) + .build(); + } +} diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml b/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml new file mode 100644 index 00000000..6287e5ab --- /dev/null +++ b/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml @@ -0,0 +1,16 @@ +# springboot related config file + +logging: + level: + com: INFO + +server: + port: 8090 + +credentials: + application: + api-key: + api-secret: + +voice: + controller-path: /voice From 27edbb3a446b1c4dd0b75404843427da45b14f4d Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <141755467+JPPortier@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:15:16 +0200 Subject: [PATCH 2/7] Update examples/tutorials/voice/handle-incoming-call/README.md Co-authored-by: Alex Sberna --- examples/tutorials/voice/handle-incoming-call/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md index 52fc66f8..07873dc0 100644 --- a/examples/tutorials/voice/handle-incoming-call/README.md +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -26,7 +26,7 @@ Located in `voice` section you can modify: #### Server port Located in `server` section: -- port: The port to be used to listen incoming request. Default: 8090 +- port: The port to be used for listening for incoming requests. Default: 8090 ### Starting server locally From c9e9db58ba1405b765e9964172400386332ac547 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <141755467+JPPortier@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:15:45 +0200 Subject: [PATCH 3/7] Update examples/tutorials/voice/handle-incoming-call/README.md Co-authored-by: Alex Sberna --- examples/tutorials/voice/handle-incoming-call/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md index 07873dc0..aa40b344 100644 --- a/examples/tutorials/voice/handle-incoming-call/README.md +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -45,7 +45,7 @@ Forwarding request to same `8090` port used above: ngrok http 8090 ``` -ngrok output will contains output like: +ngrok output will contain output like the following: ``` ngrok (Ctrl+C to quit) From b29a60547bcb23d51fb537130f17348c6782e661 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <141755467+JPPortier@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:15:51 +0200 Subject: [PATCH 4/7] Update examples/tutorials/voice/handle-incoming-call/README.md Co-authored-by: Alex Sberna --- examples/tutorials/voice/handle-incoming-call/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md index aa40b344..054c6616 100644 --- a/examples/tutorials/voice/handle-incoming-call/README.md +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -30,7 +30,7 @@ Located in `server` section: ### Starting server locally -Compile and run the application as server onto you localhost. +Compile and run the application as server on your localhost. ```bash mvn spring-boot:run ``` From e1f72da4593ea2f7b3a1e97aa05bad6d11586116 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <141755467+JPPortier@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:16:05 +0200 Subject: [PATCH 5/7] Update examples/tutorials/voice/handle-incoming-call/README.md Co-authored-by: Alex Sberna --- examples/tutorials/voice/handle-incoming-call/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md index 054c6616..8f9ff8ca 100644 --- a/examples/tutorials/voice/handle-incoming-call/README.md +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -63,7 +63,7 @@ Contains `https://0e64-78-117-86-140.ngrok-free.app` value. You have now a local web server responding to `/voice` path and `ngrok` instance running providing a bridge from `https://0e64-78-117-86-140.ngrok-free.app` onto local webserver. -Last step is now to configure your dashboard to set Voice application callback to point your local webserver. +Last step is now to configure your dashboard to set Voice application callback to point towards your local webserver. The callback to be called by sinch will be in form of: ``/`` From 26e0dcd8b5f700d1b7d3c569a5af194abe70020e Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier Date: Wed, 24 Apr 2024 08:34:03 +0200 Subject: [PATCH 6/7] PR comments --- .../tutorials/voice/handle-incoming-call/README.md | 11 +++++++---- .../src/main/java/com/mycompany/app/Config.java | 12 ++++++------ .../src/main/resources/application.yaml | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md index 8f9ff8ca..6e173b42 100644 --- a/examples/tutorials/voice/handle-incoming-call/README.md +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -8,6 +8,7 @@ This directory contains sample related to Java SDK tutorial: [Handle incoming ca - [Maven](https://maven.apache.org/) - [ngrok](https://ngrok.com/docs) - [Sinch account](https://dashboard.sinch.com) +- Having a [Sinch phone number attached to your account](https://developers.sinch.com/docs/voice/getting-started/#3-assign-your-number) ## Usage @@ -16,9 +17,9 @@ This directory contains sample related to Java SDK tutorial: [Handle incoming ca Application settings is using the SpringBoot configuration file: [`application.yaml`](src/main/resources/application.yaml) file and set: #### Sinch credentials -Located in `credentials` section (*you can find Voice application credentials you need on your [Sinch dashboard](https://dashboard.sinch.com/voice/apps)*): -- `api-id`: YOUR_Voice_Application_Id -- `api-secret`: YOUR_Voice_Application_Secret +Located in `credentials.application` section (*you can find Voice application credentials you need on your [Sinch dashboard](https://dashboard.sinch.com/voice/apps)*): +- `id`: YOUR_Voice_Application_Id +- `secret`: YOUR_Voice_Application_Secret #### Webhook controller path Located in `voice` section you can modify: @@ -73,4 +74,6 @@ e.g. According to previous sample values: https://0e64-78-117-86-140.ngrok-free. 2. Edit the application setting related to the Application ID set from config file (see [Sinch credentials](#configure-application-settings)) 3. Fill `Callback URL` field with *https://0e64-78-117-86-140.ngrok-free.app/voice* -You can now perform a to your number, the webhook will be called and serve response to callee. +You can now place a call to Sinch number attached to your Voice app (see [documentation](https://developers.sinch.com/docs/voice/getting-started/#3-assign-your-number) + +When calling your Sinch number attached to application, the Sinch platform will trigger an `Incoming Call Event (ICE)` toward your server your response will be served to the callee. \ No newline at end of file diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java index ef1cd7cb..8e5c046d 100644 --- a/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java +++ b/examples/tutorials/voice/handle-incoming-call/src/main/java/com/mycompany/app/Config.java @@ -9,11 +9,11 @@ @org.springframework.context.annotation.Configuration public class Config { - @Value("${credentials.application.api-key}") - String applicationApiKey; + @Value("${credentials.application.key}") + String applicationKey; - @Value("${credentials.application.api-secret}") - String applicationApiSecret; + @Value("${credentials.application.secret}") + String applicationSecret; @Value("${voice.controller-path}") private String controllerPath; @@ -23,8 +23,8 @@ public VoiceService voiceService() { var configuration = Configuration.builder() - .setApplicationKey(applicationApiKey) - .setApplicationSecret(applicationApiSecret) + .setApplicationKey(applicationKey) + .setApplicationSecret(applicationSecret) .build(); return new SinchClient(configuration).voice(); diff --git a/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml b/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml index 6287e5ab..0c945fed 100644 --- a/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml +++ b/examples/tutorials/voice/handle-incoming-call/src/main/resources/application.yaml @@ -9,8 +9,8 @@ server: credentials: application: - api-key: - api-secret: + key: + secret: voice: controller-path: /voice From 5832a535a5c47b1bd306597e02af2dec7fab89dd Mon Sep 17 00:00:00 2001 From: Jean-Pierre Portier <141755467+JPPortier@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:41:03 +0200 Subject: [PATCH 7/7] Update examples/tutorials/voice/handle-incoming-call/README.md --- examples/tutorials/voice/handle-incoming-call/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorials/voice/handle-incoming-call/README.md b/examples/tutorials/voice/handle-incoming-call/README.md index 6e173b42..e8faa32e 100644 --- a/examples/tutorials/voice/handle-incoming-call/README.md +++ b/examples/tutorials/voice/handle-incoming-call/README.md @@ -1,4 +1,4 @@ -# Handle Inconing Call webhook application sample +# Handle Incoming Call webhook application sample This directory contains sample related to Java SDK tutorial: [Handle incoming call](https://developers.sinch.com/docs/voice/getting-started/java/incoming-call)