From 03915e348839c495489d1cff5383948c2de60a2e Mon Sep 17 00:00:00 2001 From: Carlos Villavicencio Date: Tue, 21 Apr 2020 15:57:10 -0500 Subject: [PATCH 1/6] Code Exchange upgrade --- .env.example | 20 ++- .env.example.ps1 | 6 +- .github/workflows/maven.yml | 32 +++++ .java-version | 1 + .mergify.yml | 13 ++ CODE_OF_CONDUCT.md | 73 +++++++++++ CONTRIBUTING.md | 3 + LICENSE | 14 ++- README.md | 31 +++-- pom.xml | 6 +- src/main/java/com/twilio/Webapp.java | 30 ++--- src/main/resources/public/index.html | 4 +- src/main/resources/public/quickstart.js | 157 +++++++++++++++--------- 13 files changed, 290 insertions(+), 100 deletions(-) create mode 100644 .github/workflows/maven.yml create mode 100644 .java-version create mode 100644 .mergify.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md diff --git a/.env.example b/.env.example index 68928ce..8ed759b 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,16 @@ -export TWILIO_ACCOUNT_SID= -export TWILIO_AUTH_TOKEN= -export TWILIO_TWIML_APP_SID= -export TWILIO_CALLER_ID= +export TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +export TWILIO_CALLER_ID=+1XXXYYYZZZZ + +# SID of your TwiML Application +# https://www.twilio.com/console/voice/twiml/apps +# OR +# twilio api:core:applications:create --friendly-name=voice-client-javascript +export TWILIO_TWIML_APP_SID=APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +# Your REST API Key information +# https://www.twilio.com/console/project/api-keys +# OR +# twilio api:core:keys:create --friendly-name=voice-client-javascript -o json +# NOTE: Make sure to copy the secret, it'll will only be displayed once +export API_KEY=SKXXXXXXXXXXXX +export API_SECRET=XXXXXXXXXXXXXX diff --git a/.env.example.ps1 b/.env.example.ps1 index 99a6735..89a7009 100644 --- a/.env.example.ps1 +++ b/.env.example.ps1 @@ -1,7 +1,8 @@ $Env:TWILIO_ACCOUNT_SID = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" -$Env:TWILIO_AUTH_TOKEN = "your_auth_token" $Env:TWILIO_TWIML_APP_SID = "APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" $Env:TWILIO_CALLER_ID = "+1XXXYYYZZZZ" +$Env:API_KEY = "SKXXXXXXXXXXXX" +$Env:API_SECRET = "XXXXXXXXXXXXXXXX" # Uncomment the following if you'd like the environment variables # to be permanently set on your user account for this machine. @@ -9,8 +10,9 @@ $Env:TWILIO_CALLER_ID = "+1XXXYYYZZZZ" <# [Environment]::SetEnvironmentVariable("TWILIO_ACCOUNT_SID", $Env:TWILIO_ACCOUNT_SID, "User") -[Environment]::SetEnvironmentVariable("TWILIO_AUTH_TOKEN", $Env:TWILIO_AUTH_TOKEN, "User") [Environment]::SetEnvironmentVariable("TWILIO_TWIML_APP_SID", $Env:TWILIO_TWIML_APP_SID, "User") [Environment]::SetEnvironmentVariable("TWILIO_CALLER_ID", $Env:TWILIO_CALLER_ID, "User") +[Environment]::SetEnvironmentVariable("API_KEY", $Env:API_KEY, "User") +[Environment]::SetEnvironmentVariable("API_SECRET", $Env:API_SECRET, "User") #> \ No newline at end of file diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..90f17f4 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,32 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11.0 + - name: Build with Maven + run: mvn compile + - name: Test + run: mvn clean test + env: + TWILIO_ACCOUNT_SID: ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + TWILIO_CALLER_ID: +1XXXYYYZZZZ + TWILIO_TWIML_APP_SID: APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + API_KEY: SKXXXXXXXXXXXX + API_SECRET: XXXXXXXXXXXXXX diff --git a/.java-version b/.java-version new file mode 100644 index 0000000..2dbc24b --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +11.0 diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..0433495 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,13 @@ +pull_request_rules: + - name: automatic merge for Dependabot pull requests + conditions: + - author=dependabot-preview[bot] + - status-success=build (macos-latest, 10) + - status-success=build (macos-latest, 12) + - status-success=build (windows-latest, 10) + - status-success=build (windows-latest, 12) + - status-success=build (ubuntu-latest, 10) + - status-success=build (ubuntu-latest, 12) + actions: + merge: + method: squash diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2f0727e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at open-source@twilio.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ad3257e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to Twilio + +All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. diff --git a/LICENSE b/LICENSE index 4bcc4c2..2c3cf9d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2015 Twilio Inc. +MIT License + +Copyright (c) 2020 Twilio Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 98f659a..37e9756 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,28 @@ + + Twilio + + # Twilio Client Quickstart for Java +![](https://github.com/TwilioDevEd/client-quickstart-java/workflows/Java/badge.svg) + > We are currently in the process of updating this sample template. If you are encountering any issues with the sample, please open an issue at [github.com/twilio-labs/code-exchange/issues](https://github.com/twilio-labs/code-exchange/issues) and we'll try to help you. This application should give you a ready-made starting point for writing your own voice apps with Twilio Client. Before we begin, we need to collect all the config values we need to run the application: -| Config Value | Description | +| Config Value | Description | | :------------- |:------------- | -Account SID | Your primary Twilio account identifier - find this [in the console here](https://www.twilio.com/console). -Auth Token | Used to authenticate - [just like the above, you'll find this here](https://www.twilio.com/console). -TwiML App SID | The TwiML application with a voice URL configured to access your server running this app - create one [in the console here](https://www.twilio.com//console/phone-numbers/dev-tools/twiml-apps). Also, you will need to configure the Voice "REQUEST URL" on the TwiML app once you've got your server up and running. -Twilio Phone # | A Twilio phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164) - you can [get one here](https://www.twilio.com/console/phone-numbers/incoming) +`TWILIO_ACCOUNT_SID` | Your primary Twilio account identifier - find this [in the console here](https://www.twilio.com/console). +`TWILIO_TWIML_APP_SID` | The TwiML application with a voice URL configured to access your server running this app - create one [in the console here](https://www.twilio.com/console/voice/twiml/apps). Also, you will need to configure the Voice "REQUEST URL" on the TwiML app once you've got your server up and running. +`TWILIO_CALLER_ID` | A Twilio phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164) - you can [get one here](https://www.twilio.com/console/phone-numbers/incoming) +`API_KEY` / `API_SECRET` | Your REST API Key information needed to create an [Access Token](https://www.twilio.com/docs/iam/access-tokens) - create [one here](https://www.twilio.com/console/project/api-keys). ## Setting Up The Java Application This application uses the lightweight [Spark Framework](www.sparkjava.com), and -requires Java 8 and [Maven](https://maven.apache.org/install.html). +requires Java 8 and [Maven](https://maven.apache.org/install.html). ### Mac & Linux @@ -26,7 +32,7 @@ Begin by creating a configuration file for your application: cp .env.example .env ``` -Edit `.env` with the four configuration parameters we gathered from above. Export +Edit `.env` with the five configuration parameters we gathered from above. Export the configuration in this file as system environment variables like so on Unix based systems: @@ -74,6 +80,8 @@ There's just a few more steps to get Twilio's voice infrastructure talking to yo 1. [Download and install ngrok](https://ngrok.com/download) +> [Learn 6 awesome reasons why to use ngrok](https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html). + 2. Run ngrok: ```bash @@ -88,6 +96,8 @@ Voice "REQUEST URL" to be your ngrok URL plus `/voice`. For example: ![screenshot of twiml app](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/TwilioClientRequestUrl.original.png) +> **Note:** You must set your webhook urls to the `https` ngrok tunnel created. + You should now be ready to rock! Make some phone calls. Open it on another device and call yourself. Note that Twilio Client requires WebRTC enabled browsers, so Edge and Internet Explorer will not work for testing. @@ -95,6 +105,9 @@ We'd recommend Google Chrome or Mozilla Firefox instead. ![screenshot of phone app](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/TwilioClientQuickstart.original.png) -## License +## Meta -MIT +* No warranty expressed or implied. Software is as is. Diggity. +* The CodeExchange repository can be found [here](https://github.com/twilio-labs/code-exchange/). +* [MIT License](http://www.opensource.org/licenses/mit-license.html) +* Lovingly crafted by Twilio Developer Education. diff --git a/pom.xml b/pom.xml index a9ecf01..5ff6d47 100644 --- a/pom.xml +++ b/pom.xml @@ -32,17 +32,17 @@ com.github.javafaker javafaker - 1.0.1 + 1.0.2 com.twilio.sdk twilio - 7.44.0 + 7.49.1 ch.qos.logback logback-classic - 1.2.2 + 1.2.3 diff --git a/src/main/java/com/twilio/Webapp.java b/src/main/java/com/twilio/Webapp.java index 313541c..3ebb00a 100644 --- a/src/main/java/com/twilio/Webapp.java +++ b/src/main/java/com/twilio/Webapp.java @@ -5,19 +5,14 @@ import static spark.Spark.staticFileLocation; import static spark.Spark.afterAfter; -import java.util.ArrayList; -import java.util.List; import java.util.HashMap; import com.github.javafaker.Faker; import com.google.gson.Gson; // Token generation imports -import com.twilio.jwt.Jwt; -import com.twilio.jwt.client.ClientCapability; -import com.twilio.jwt.client.IncomingClientScope; -import com.twilio.jwt.client.OutgoingClientScope; -import com.twilio.jwt.client.Scope; +import com.twilio.jwt.accesstoken.AccessToken; +import com.twilio.jwt.accesstoken.VoiceGrant; // TwiML generation imports import com.twilio.twiml.VoiceResponse; @@ -41,17 +36,24 @@ public static void main(String[] args) { // Create a capability token using our Twilio credentials get("/token", "application/json", (request, response) -> { String acctSid = System.getenv("TWILIO_ACCOUNT_SID"); - String authToken = System.getenv("TWILIO_AUTH_TOKEN"); String applicationSid = System.getenv("TWILIO_TWIML_APP_SID"); + String apiKey = System.getenv("API_KEY"); + String apiSecret = System.getenv("API_SECRET"); // Generate a random username for the connecting client String identity = faker.name().firstName() + faker.address().zipCode(); - // Generate capability token - List scopes = new ArrayList<>(); - scopes.add(new IncomingClientScope(identity)); - scopes.add(new OutgoingClientScope.Builder(applicationSid).build()); - Jwt jwt = new ClientCapability.Builder(acctSid, authToken).scopes(scopes).build(); - String token = jwt.toJwt(); + // Create Voice grant + VoiceGrant grant = new VoiceGrant(); + grant.setOutgoingApplicationSid(applicationSid); + + // Optional: add to allow incoming calls + grant.setIncomingAllow(true); + + // Create access token + AccessToken accessToken = new AccessToken.Builder(acctSid, apiKey, apiSecret) + .identity(identity).grant(grant).build(); + + String token = accessToken.toJwt(); // create JSON response payload HashMap json = new HashMap<>(); diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html index 7611685..31215ae 100644 --- a/src/main/resources/public/index.html +++ b/src/main/resources/public/index.html @@ -32,8 +32,8 @@
- - + + diff --git a/src/main/resources/public/quickstart.js b/src/main/resources/public/quickstart.js index 74c8a9f..81e8732 100644 --- a/src/main/resources/public/quickstart.js +++ b/src/main/resources/public/quickstart.js @@ -7,44 +7,59 @@ log('Requesting Capability Token...'); $.getJSON('/token') - .done(function (data) { - log('Got a token.'); - console.log('Token: ' + data.token); + .then(function (data) { + log("Got a token."); + console.log("Token: " + data.token); // Setup Twilio.Device - Twilio.Device.setup(data.token); + device = new Twilio.Device(data.token, { + // Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and + // providing better audio quality in restrained network conditions. Opus will be default in 2.0. + codecPreferences: ["opus", "pcmu"], + // Use fake DTMF tones client-side. Real tones are still sent to the other end of the call, + // but the client-side DTMF tones are fake. This prevents the local mic capturing the DTMF tone + // a second time and sending the tone twice. This will be default in 2.0. + fakeLocalDTMF: true, + // Use `enableRingingState` to enable the device to emit the `ringing` + // state. The TwiML backend also needs to have the attribute + // `answerOnBridge` also set to true in the `Dial` verb. This option + // changes the behavior of the SDK to consider a call `ringing` starting + // from the connection to the TwiML backend to when the recipient of + // the `Dial` verb answers. + enableRingingState: true + }); - Twilio.Device.ready(function (device) { - log('Twilio.Device Ready!'); - document.getElementById('call-controls').style.display = 'block'; + device.on("ready", function (device) { + log("Twilio.Device Ready!"); + document.getElementById("call-controls").style.display = "block"; }); - Twilio.Device.error(function (error) { - log('Twilio.Device Error: ' + error.message); + device.on("error", function (error) { + log("Twilio.Device Error: " + error.message); }); - Twilio.Device.connect(function (conn) { - log('Successfully established call!'); - document.getElementById('button-call').style.display = 'none'; - document.getElementById('button-hangup').style.display = 'inline'; - volumeIndicators.style.display = 'block'; + device.on("connect", function (conn) { + log("Successfully established call!"); + document.getElementById("button-call").style.display = "none"; + document.getElementById("button-hangup").style.display = "inline"; + volumeIndicators.style.display = "block"; bindVolumeIndicators(conn); }); - Twilio.Device.disconnect(function (conn) { - log('Call ended.'); - document.getElementById('button-call').style.display = 'inline'; - document.getElementById('button-hangup').style.display = 'none'; - volumeIndicators.style.display = 'none'; + device.on("disconnect", function (conn) { + log("Call ended."); + document.getElementById("button-call").style.display = "inline"; + document.getElementById("button-hangup").style.display = "none"; + volumeIndicators.style.display = "none"; }); - Twilio.Device.incoming(function (conn) { - log('Incoming connection from ' + conn.parameters.From); - var archEnemyPhoneNumber = '+12099517118'; + device.on("incoming", function (conn) { + log("Incoming connection from " + conn.parameters.From); + var archEnemyPhoneNumber = "+12093373517"; if (conn.parameters.From === archEnemyPhoneNumber) { conn.reject(); - log('It\'s your nemesis. Rejected call.'); + log("It's your nemesis. Rejected call."); } else { // accept the incoming connection and start two-way audio conn.accept(); @@ -53,53 +68,72 @@ setClientNameUI(data.identity); - Twilio.Device.audio.on('deviceChange', updateAllDevices); + device.audio.on("deviceChange", updateAllDevices.bind(device)); // Show audio selection UI if it is supported by the browser. - if (Twilio.Device.audio.isSelectionSupported) { - document.getElementById('output-selection').style.display = 'block'; + if (device.audio.isOutputSelectionSupported) { + document.getElementById("output-selection").style.display = "block"; } }) - .fail(function () { - log('Could not get a token from server!'); + .catch(function (err) { + console.log(err); + log("Could not get a token from server!"); }); // Bind button to make call - document.getElementById('button-call').onclick = function () { + document.getElementById("button-call").onclick = function () { // get the phone number to connect the call to var params = { - To: document.getElementById('phone-number').value + To: document.getElementById("phone-number").value }; - console.log('Calling ' + params.To + '...'); - Twilio.Device.connect(params); + console.log("Calling " + params.To + "..."); + if (device) { + var outgoingConnection = device.connect(params); + outgoingConnection.on("ringing", function () { + log("Ringing..."); + }); + } }; // Bind button to hangup call - document.getElementById('button-hangup').onclick = function () { - log('Hanging up...'); - Twilio.Device.disconnectAll(); + document.getElementById("button-hangup").onclick = function () { + log("Hanging up..."); + if (device) { + device.disconnectAll(); + } }; - document.getElementById('get-devices').onclick = function() { - navigator.mediaDevices.getUserMedia({ audio: true }) - .then(updateAllDevices); + document.getElementById("get-devices").onclick = function () { + navigator.mediaDevices + .getUserMedia({ audio: true }) + .then(updateAllDevices.bind(device)); }; - speakerDevices.addEventListener('change', function() { - var selectedDevices = [].slice.call(speakerDevices.children) - .filter(function(node) { return node.selected; }) - .map(function(node) { return node.getAttribute('data-id'); }); - - Twilio.Device.audio.speakerDevices.set(selectedDevices); + speakerDevices.addEventListener("change", function () { + var selectedDevices = [].slice + .call(speakerDevices.children) + .filter(function (node) { + return node.selected; + }) + .map(function (node) { + return node.getAttribute("data-id"); + }); + + device.audio.speakerDevices.set(selectedDevices); }); - ringtoneDevices.addEventListener('change', function() { - var selectedDevices = [].slice.call(ringtoneDevices.children) - .filter(function(node) { return node.selected; }) - .map(function(node) { return node.getAttribute('data-id'); }); - - Twilio.Device.audio.ringtoneDevices.set(selectedDevices); + ringtoneDevices.addEventListener("change", function () { + var selectedDevices = [].slice + .call(ringtoneDevices.children) + .filter(function (node) { + return node.selected; + }) + .map(function (node) { + return node.getAttribute("data-id"); + }); + + device.audio.ringtoneDevices.set(selectedDevices); }); function bindVolumeIndicators(connection) { @@ -127,25 +161,28 @@ } function updateAllDevices() { - updateDevices(speakerDevices, Twilio.Device.audio.speakerDevices.get()); - updateDevices(ringtoneDevices, Twilio.Device.audio.ringtoneDevices.get()); + updateDevices(speakerDevices, device.audio.speakerDevices.get()); + updateDevices(ringtoneDevices, device.audio.ringtoneDevices.get()); } }); // Update the available ringtone and speaker devices function updateDevices(selectEl, selectedDevices) { - selectEl.innerHTML = ''; - Twilio.Device.audio.availableOutputDevices.forEach(function(device, id) { - var isActive = (selectedDevices.size === 0 && id === 'default'); - selectedDevices.forEach(function(device) { - if (device.deviceId === id) { isActive = true; } + selectEl.innerHTML = ""; + + device.audio.availableOutputDevices.forEach(function (device, id) { + var isActive = selectedDevices.size === 0 && id === "default"; + selectedDevices.forEach(function (device) { + if (device.deviceId === id) { + isActive = true; + } }); - var option = document.createElement('option'); + var option = document.createElement("option"); option.label = device.label; - option.setAttribute('data-id', id); + option.setAttribute("data-id", id); if (isActive) { - option.setAttribute('selected', 'selected'); + option.setAttribute("selected", "selected"); } selectEl.appendChild(option); }); From 157a0798ce0112ab25d0ac383370645c8b7d1fb8 Mon Sep 17 00:00:00 2001 From: Carlos Villavicencio Date: Wed, 22 Apr 2020 10:15:54 -0500 Subject: [PATCH 2/6] Add tests --- .env.example | 4 +- .github/workflows/maven.yml | 4 +- .gitignore | 1 + pom.xml | 29 +++++- src/main/java/com/twilio/Webapp.java | 113 +++++++++++++---------- src/test/java/com/twilio/WebappTest.java | 26 ++++++ 6 files changed, 122 insertions(+), 55 deletions(-) create mode 100644 src/test/java/com/twilio/WebappTest.java diff --git a/.env.example b/.env.example index 8ed759b..dc4070d 100644 --- a/.env.example +++ b/.env.example @@ -12,5 +12,5 @@ export TWILIO_TWIML_APP_SID=APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # OR # twilio api:core:keys:create --friendly-name=voice-client-javascript -o json # NOTE: Make sure to copy the secret, it'll will only be displayed once -export API_KEY=SKXXXXXXXXXXXX -export API_SECRET=XXXXXXXXXXXXXX +export API_KEY=SKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +export API_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 90f17f4..d005371 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -28,5 +28,5 @@ jobs: TWILIO_ACCOUNT_SID: ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX TWILIO_CALLER_ID: +1XXXYYYZZZZ TWILIO_TWIML_APP_SID: APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - API_KEY: SKXXXXXXXXXXXX - API_SECRET: XXXXXXXXXXXXXX + API_KEY: SKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + API_SECRET: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/.gitignore b/.gitignore index b795a15..1ce08fe 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties +.vscode *.pydevproject .metadata diff --git a/pom.xml b/pom.xml index 5ff6d47..7e3b86e 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ 1.8 1.8 + 5.6.2 @@ -44,6 +45,24 @@ logback-classic 1.2.3 + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + @@ -51,7 +70,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.3 + 3.2.3 true @@ -82,6 +101,14 @@ + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + diff --git a/src/main/java/com/twilio/Webapp.java b/src/main/java/com/twilio/Webapp.java index 3ebb00a..dce1c7d 100644 --- a/src/main/java/com/twilio/Webapp.java +++ b/src/main/java/com/twilio/Webapp.java @@ -22,77 +22,90 @@ import com.twilio.twiml.voice.Say; public class Webapp { + + public static String generateIdentity() { + // Create a Faker instance to generate a random username for the connecting user + Faker faker = new Faker(); + return faker.name().firstName() + faker.address().zipCode(); + } + + public static String createJsonAccessToken(String identity) { + String acctSid = System.getenv("TWILIO_ACCOUNT_SID"); + String applicationSid = System.getenv("TWILIO_TWIML_APP_SID"); + String apiKey = System.getenv("API_KEY"); + String apiSecret = System.getenv("API_SECRET"); + // Create Voice grant + VoiceGrant grant = new VoiceGrant(); + grant.setOutgoingApplicationSid(applicationSid); + + // Optional: add to allow incoming calls + grant.setIncomingAllow(true); + + // Create access token + AccessToken accessToken = new AccessToken.Builder(acctSid, apiKey, apiSecret).identity(identity).grant(grant) + .build(); + + String token = accessToken.toJwt(); + + // create JSON response payload + HashMap json = new HashMap<>(); + json.put("identity", identity); + json.put("token", token); + + Gson gson = new Gson(); + return gson.toJson(json); + } + + public static String createVoiceResponse(String to) { + VoiceResponse voiceTwimlResponse; + + if (to != null) { + Dial.Builder dialBuilder = new Dial.Builder() + .callerId(System.getenv("TWILIO_CALLER_ID")); + + // wrap the phone number or client name in the appropriate TwiML verb + // by checking if the number given has only digits and format symbols + if(to.matches("^[\\d\\+\\-\\(\\) ]+$")) { + dialBuilder = dialBuilder.number(new Number.Builder(to).build()); + } else { + dialBuilder = dialBuilder.client(new Client.Builder(to).build()); + } + + voiceTwimlResponse = new VoiceResponse.Builder() + .dial(dialBuilder.build()) + .build(); + } else { + voiceTwimlResponse = new VoiceResponse.Builder() + .say(new Say.Builder("Thanks for calling!").build()) + .build(); + } + + return voiceTwimlResponse.toXml(); + } public static void main(String[] args) { // Serve static files from src/main/resources/public staticFileLocation("/public"); - // Create a Faker instance to generate a random username for the connecting user - Faker faker = new Faker(); - // Log all requests and responses afterAfter(new LoggingFilter()); // Create a capability token using our Twilio credentials get("/token", "application/json", (request, response) -> { - String acctSid = System.getenv("TWILIO_ACCOUNT_SID"); - String applicationSid = System.getenv("TWILIO_TWIML_APP_SID"); - String apiKey = System.getenv("API_KEY"); - String apiSecret = System.getenv("API_SECRET"); // Generate a random username for the connecting client - String identity = faker.name().firstName() + faker.address().zipCode(); - - // Create Voice grant - VoiceGrant grant = new VoiceGrant(); - grant.setOutgoingApplicationSid(applicationSid); - - // Optional: add to allow incoming calls - grant.setIncomingAllow(true); - - // Create access token - AccessToken accessToken = new AccessToken.Builder(acctSid, apiKey, apiSecret) - .identity(identity).grant(grant).build(); - - String token = accessToken.toJwt(); - - // create JSON response payload - HashMap json = new HashMap<>(); - json.put("identity", identity); - json.put("token", token); + String identity = Webapp.generateIdentity(); // Render JSON response response.header("Content-Type", "application/json"); - Gson gson = new Gson(); - return gson.toJson(json); + return Webapp.createJsonAccessToken(identity); }); // Generate voice TwiML post("/voice", "application/x-www-form-urlencoded", (request, response) -> { - VoiceResponse voiceTwimlResponse; String to = request.queryParams("To"); - if (to != null) { - Dial.Builder dialBuilder = new Dial.Builder() - .callerId(System.getenv("TWILIO_CALLER_ID")); - - // wrap the phone number or client name in the appropriate TwiML verb - // by checking if the number given has only digits and format symbols - if(to.matches("^[\\d\\+\\-\\(\\) ]+$")) { - dialBuilder = dialBuilder.number(new Number.Builder(to).build()); - } else { - dialBuilder = dialBuilder.client(new Client.Builder(to).build()); - } - - voiceTwimlResponse = new VoiceResponse.Builder() - .dial(dialBuilder.build()) - .build(); - } else { - voiceTwimlResponse = new VoiceResponse.Builder() - .say(new Say.Builder("Thanks for calling!").build()) - .build(); - } response.header("Content-Type", "text/xml"); - return voiceTwimlResponse.toXml(); + return Webapp.createVoiceResponse(to); }); } } diff --git a/src/test/java/com/twilio/WebappTest.java b/src/test/java/com/twilio/WebappTest.java new file mode 100644 index 0000000..d777a4f --- /dev/null +++ b/src/test/java/com/twilio/WebappTest.java @@ -0,0 +1,26 @@ +package com.twilio; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class WebappTest { + @Test + void testDoCreateJsonAccessTokenReturnValidString() { + String token = Webapp.createJsonAccessToken("nezuko"); + assertTrue(token.contains("\"identity\":\"nezuko\"")); + assertTrue(token.contains("\"token\":")); + } + + @Test + void testVoiceResponseHasToParameter() { + String response = Webapp.createVoiceResponse("+5939999999"); + assertTrue(response.contains("+5939999999")); + } + + @Test + void testVoiceResponseHasNoParameters() { + String response = Webapp.createVoiceResponse(null); + assertTrue(response.contains("Thanks for calling!")); + } +} From 3db1ce82de33bb0b2f29eb73706b92eda1c3b3da Mon Sep 17 00:00:00 2001 From: Carlos Villavicencio Date: Wed, 20 May 2020 15:15:53 -0500 Subject: [PATCH 3/6] Add makefile and update readme template --- .github/workflows/maven.yml | 4 +- Makefile | 6 ++ README.md | 171 +++++++++++++++++++++++------------- 3 files changed, 119 insertions(+), 62 deletions(-) create mode 100644 Makefile diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d005371..dc70e89 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,9 +5,9 @@ name: Java on: push: - branches: [ master ] + branches: [ master, next ] pull_request: - branches: [ master ] + branches: [ master, next ] jobs: build: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a3a9b93 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +install: + mvn package + +serve: + . .env + java -jar target/client-quickstart-1.0-SNAPSHOT.jar diff --git a/README.md b/README.md index 37e9756..59093bb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - - Twilio + +Twilio # Twilio Client Quickstart for Java @@ -8,8 +8,32 @@ > We are currently in the process of updating this sample template. If you are encountering any issues with the sample, please open an issue at [github.com/twilio-labs/code-exchange/issues](https://github.com/twilio-labs/code-exchange/issues) and we'll try to help you. +## About + +Implementations in other languages: + +| .NET | Python | Node | PHP | Ruby | +| :--- | :--- | :----- | :-- | :--- | +| [Done](https://github.com/TwilioDevEd/client-quickstart-csharp) | [Done](https://github.com/TwilioDevEd/client-quickstart-python) | [Done](https://github.com/TwilioDevEd/client-quickstart-node) | [Done](https://github.com/TwilioDevEd/client-quickstart-php) | [Done](https://github.com/TwilioDevEd/client-quickstart-ruby) | + + + +## Set up + +### Requirements + +- [Java Development Kit](https://adoptopenjdk.net/) version 11 or later. +- [ngrok](https://ngrok.com) +- A Twilio account - [sign up](https://www.twilio.com/try-twilio) + +### Twilio Account Settings + This application should give you a ready-made starting point for writing your -own voice apps with Twilio Client. Before we begin, we need to collect +own appointment reminder application. Before we begin, we need to collect all the config values we need to run the application: | Config Value | Description | @@ -19,95 +43,122 @@ all the config values we need to run the application: `TWILIO_CALLER_ID` | A Twilio phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164) - you can [get one here](https://www.twilio.com/console/phone-numbers/incoming) `API_KEY` / `API_SECRET` | Your REST API Key information needed to create an [Access Token](https://www.twilio.com/docs/iam/access-tokens) - create [one here](https://www.twilio.com/console/project/api-keys). -## Setting Up The Java Application +### Local development -This application uses the lightweight [Spark Framework](www.sparkjava.com), and -requires Java 8 and [Maven](https://maven.apache.org/install.html). +After the above requirements have been met: -### Mac & Linux +1. Clone this repository and `cd` into it -Begin by creating a configuration file for your application: + ```bash + git clone git@github.com:TwilioDevEd/client-quickstart-java.git + cd client-quickstart-java + ``` -```bash -cp .env.example .env -``` +2. Set your environment variables -Edit `.env` with the five configuration parameters we gathered from above. Export -the configuration in this file as system environment variables like so on Unix -based systems: + ```bash + cp .env.example .env + ``` + See [Twilio Account Settings](#twilio-account-settings) to locate the necessary environment variables. -```bash -source .env -``` + If you are using a UNIX operating system, when the application starts the environment variables will be loaded. _If you are using a different operating system, make sure that all the + variables from the `.env` file are loaded into your environment._ -### Windows (PowerShell) + If you are using Windows (Powershell): -Begin by creating a configuration file for your application: + ```powershell + cp .env.example.ps1 .env.ps1 + ``` -```powershell -cp .env.example.ps1 .env.ps1 -``` + Edit `.env.ps1` with the four configuration parameters we gathered from above. "Dot-source" the file in PowerShell like so: -Edit `.env.ps1` with the four configuration parameters we gathered from above. -"Dot-source" the file in PowerShell like so: + ```powershell + . .\.env.ps1 + ``` -```powershell -. .\.env.ps1 -``` + This assumes you will run the application in the same PowerShell session. If not, edit the `.env.ps1` and uncomment the `[Environment]::SetEnvironmentVariable` calls. After re-running the script, the environment variables will be peramently set for your user account. -This assumes you will run the application in the same PowerShell session. If not, -edit the `.env.ps1` and uncomment the `[Environment]::SetEnvironmentVariable` calls. -After re-running the script, the environment variables will be peramently set for -your user account. +3. Build the project -## All Platforms + ```bash + make install + ``` + **NOTE:** Running the build task will also run the tests -Next, we need to install our depenedencies from Maven and compile our application code: +4. Run the application -```bash -mvn package -``` + ```bash + make serve + ``` + **NOTE:** If you are using a dedicated Java IDE like Eclipse or IntelliJ, you can start the application within the IDE and it will start in development mode, which means any changes on a source file will be automatically reloaded. + +7. Navigate to [http://localhost:4567](http://localhost:4567) + +That's it! + +### Tests -Run the application using the `java -jar` command. +You can run the tests locally by typing: ```bash -java -jar target/client-quickstart-1.0-SNAPSHOT.jar +mvn clean test ``` -Your application should now be running at [http://localhost:4567](http://localhost:4567). +### Configure Twilio -There's just a few more steps to get Twilio's voice infrastructure talking to your server. +To let our Twilio Phone number use the callback endpoint we exposed our development server will need to be publicly accessible. [We recommend using ngrok to solve this problem](https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html). -1. [Download and install ngrok](https://ngrok.com/download) +To start using `ngrok` in our project you'll have to execute the following line in the _command prompt_. -> [Learn 6 awesome reasons why to use ngrok](https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html). +``` +ngrok http 4567 -host-header="localhost:4567" +``` -2. Run ngrok: +Keep in mind that our endpoint is: - ```bash - ngrok http 4567 - ``` - -3. When ngrok starts up, it will assign a unique URL to your tunnel. -It might be something like `https://asdf456.ngrok.io`. Take note of this. +``` +http://.ngrok.io/voice +``` -4. [Configure your TwiML app](https://www.twilio.com//console/phone-numbers/dev-tools/twiml-apps)'s +[Configure your TwiML app](https://www.twilio.com//console/phone-numbers/dev-tools/twiml-apps)'s Voice "REQUEST URL" to be your ngrok URL plus `/voice`. For example: ![screenshot of twiml app](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/TwilioClientRequestUrl.original.png) +You should now be ready to rock! Make some phone calls. Open it on another device and call yourself. Note that Twilio Client requires WebRTC enabled browsers, so Edge and Internet Explorer will not work for testing. We'd recommend Google Chrome or Mozilla Firefox instead. + +![screenshot of phone app](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/TwilioClientQuickstart.original.png) + > **Note:** You must set your webhook urls to the `https` ngrok tunnel created. -You should now be ready to rock! Make some phone calls. -Open it on another device and call yourself. Note that Twilio Client requires -WebRTC enabled browsers, so Edge and Internet Explorer will not work for testing. -We'd recommend Google Chrome or Mozilla Firefox instead. +### Cloud deployment -![screenshot of phone app](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/TwilioClientQuickstart.original.png) +Additionally to trying out this application locally, you can deploy it to a variety of host services. Here is a small selection of them. + +Please be aware that some of these might charge you for the usage or might make the source code for this application visible to the public. When in doubt research the respective hosting service first. + +| Service | | +| :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [Heroku](https://www.heroku.com/) | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/TwilioDevEd/client-quickstart-java/tree/master) | + +**Some notes:** +- For Heroku, please [check this](https://devcenter.heroku.com/articles/deploying-gradle-apps-on-heroku) to properly configure the project for deployment. +- You can also follow [this guide](https://vaadin.com/blog/how-to-deploy-your-java-app-to-the-cloud) to deploy the application to several other cloud services including Google Cloud, Oracle Cloud, etc. + +## Resources + +- The CodeExchange repository can be found [here](https://github.com/twilio-labs/code-exchange/). + +## Contributing + +This template is open source and welcomes contributions. All contributions are subject to our [Code of Conduct](https://github.com/twilio-labs/.github/blob/master/CODE_OF_CONDUCT.md). + +## License + +[MIT](http://www.opensource.org/licenses/mit-license.html) + +## Disclaimer -## Meta +No warranty expressed or implied. Software is as is. -* No warranty expressed or implied. Software is as is. Diggity. -* The CodeExchange repository can be found [here](https://github.com/twilio-labs/code-exchange/). -* [MIT License](http://www.opensource.org/licenses/mit-license.html) -* Lovingly crafted by Twilio Developer Education. +[twilio]: https://www.twilio.com From 56040cfee69ea4a16b27f9132a9d3f13388a520f Mon Sep 17 00:00:00 2001 From: Carlos Villavicencio Date: Fri, 29 May 2020 11:01:09 -0500 Subject: [PATCH 4/6] Update Makefile --- Makefile | 1 - README.md | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a3a9b93..d18cc85 100644 --- a/Makefile +++ b/Makefile @@ -2,5 +2,4 @@ install: mvn package serve: - . .env java -jar target/client-quickstart-1.0-SNAPSHOT.jar diff --git a/README.md b/README.md index 59093bb..afac8d6 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,13 @@ After the above requirements have been met: ``` See [Twilio Account Settings](#twilio-account-settings) to locate the necessary environment variables. - If you are using a UNIX operating system, when the application starts the environment variables will be loaded. _If you are using a different operating system, make sure that all the - variables from the `.env` file are loaded into your environment._ + If you are using a UNIX operating system, load the environment variables before the application starts. + + ```bash + source .env + ``` + + _If you are using a different operating system, make sure that all the variables from the `.env` file are loaded into your environment._ If you are using Windows (Powershell): From 2d5dab708b98db1cd616761c6bf0b543364ce90c Mon Sep 17 00:00:00 2001 From: Carlos Villavicencio Date: Wed, 10 Jun 2020 16:08:11 -0500 Subject: [PATCH 5/6] Add Docker. Change port to 8080 --- .dockerignore | 5 +++++ Dockerfile | 23 +++++++++++++++++++++++ README.md | 13 +++++++++++-- docker-compose.yml | 14 ++++++++++++++ src/main/java/com/twilio/Webapp.java | 14 +++++++++----- 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0705b0d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +bin/ +build/ +*.db +*.sqlite +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5d6264e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM maven:3-openjdk-11 + +WORKDIR /usr/app/ + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y build-essential + +COPY . . + +# No need to update these values, only at docker-compose.yml +# If the application can't read those values, you can try update them here as well +ENV TWILIO_ACCOUNT_SID=your_account_sid +ENV TWILIO_CALLER_ID=+1XXXYYYZZZZ +ENV TWILIO_TWIML_APP_SID=APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV API_KEY=SKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV API_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +RUN make install + +EXPOSE 8080 + +CMD ["make", "serve"] diff --git a/README.md b/README.md index afac8d6..0635ac4 100644 --- a/README.md +++ b/README.md @@ -97,10 +97,19 @@ After the above requirements have been met: ``` **NOTE:** If you are using a dedicated Java IDE like Eclipse or IntelliJ, you can start the application within the IDE and it will start in development mode, which means any changes on a source file will be automatically reloaded. -7. Navigate to [http://localhost:4567](http://localhost:4567) +7. Navigate to [http://localhost:8080](http://localhost:8080) That's it! +### Docker + +If you have [Docker](https://www.docker.com/) already installed on your machine, you can use our `docker-compose.yml` to setup your project. + +1. Make sure you have the project cloned. +2. Setup the environmental variables in the `docker-compose.yml` file, see the [Twilio Account Settings](#twilio-account-settings). +3. Run `docker-compose --env-file /dev/null up`. +4. Follow the steps in [Configure Twilio](#configure-twilio) section on how to expose your port to Twilio using a tool like [ngrok](https://ngrok.com/) and configure the remaining parts of your application. + ### Tests You can run the tests locally by typing: @@ -116,7 +125,7 @@ To let our Twilio Phone number use the callback endpoint we exposed our developm To start using `ngrok` in our project you'll have to execute the following line in the _command prompt_. ``` -ngrok http 4567 -host-header="localhost:4567" +ngrok http 8080 -host-header="localhost:8080" ``` Keep in mind that our endpoint is: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4ed9179 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.6" +services: + app: + stdin_open: true + tty: true + environment: + - TWILIO_ACCOUNT_SID=your_account_sid + - TWILIO_CALLER_ID=+1XXXYYYZZZZ + - TWILIO_TWIML_APP_SID=APXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + - API_KEY=SKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + - API_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + build: . + ports: + - "8080:8080" diff --git a/src/main/java/com/twilio/Webapp.java b/src/main/java/com/twilio/Webapp.java index dce1c7d..a56fa8f 100644 --- a/src/main/java/com/twilio/Webapp.java +++ b/src/main/java/com/twilio/Webapp.java @@ -2,6 +2,7 @@ import static spark.Spark.get; import static spark.Spark.post; +import static spark.Spark.port; import static spark.Spark.staticFileLocation; import static spark.Spark.afterAfter; @@ -37,16 +38,16 @@ public static String createJsonAccessToken(String identity) { // Create Voice grant VoiceGrant grant = new VoiceGrant(); grant.setOutgoingApplicationSid(applicationSid); - + // Optional: add to allow incoming calls grant.setIncomingAllow(true); - + // Create access token AccessToken accessToken = new AccessToken.Builder(acctSid, apiKey, apiSecret).identity(identity).grant(grant) - .build(); - + .build(); + String token = accessToken.toJwt(); - + // create JSON response payload HashMap json = new HashMap<>(); json.put("identity", identity); @@ -84,6 +85,9 @@ public static String createVoiceResponse(String to) { } public static void main(String[] args) { + // Default port 8080 + port(8080); + // Serve static files from src/main/resources/public staticFileLocation("/public"); From 549b3fc1bdd2b30ce4ed1a2d3be091b4b236c72c Mon Sep 17 00:00:00 2001 From: Carlos Villavicencio Date: Wed, 17 Jun 2020 09:43:13 -0500 Subject: [PATCH 6/6] Add CI --- .github/workflows/maven.yml | 6 +++++- .mergify.yml | 9 +++------ src/main/java/com/twilio/Webapp.java | 29 ++++++++++++++++++---------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index dc70e89..0bdad12 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -12,7 +12,11 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.platform }} + strategy: + max-parallel: 3 + matrix: + platform: [windows-latest, macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/.mergify.yml b/.mergify.yml index 0433495..91278e5 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -2,12 +2,9 @@ pull_request_rules: - name: automatic merge for Dependabot pull requests conditions: - author=dependabot-preview[bot] - - status-success=build (macos-latest, 10) - - status-success=build (macos-latest, 12) - - status-success=build (windows-latest, 10) - - status-success=build (windows-latest, 12) - - status-success=build (ubuntu-latest, 10) - - status-success=build (ubuntu-latest, 12) + - status-success=build (macos-latest) + - status-success=build (windows-latest) + - status-success=build (ubuntu-latest) actions: merge: method: squash diff --git a/src/main/java/com/twilio/Webapp.java b/src/main/java/com/twilio/Webapp.java index a56fa8f..b13240e 100644 --- a/src/main/java/com/twilio/Webapp.java +++ b/src/main/java/com/twilio/Webapp.java @@ -43,8 +43,10 @@ public static String createJsonAccessToken(String identity) { grant.setIncomingAllow(true); // Create access token - AccessToken accessToken = new AccessToken.Builder(acctSid, apiKey, apiSecret).identity(identity).grant(grant) - .build(); + AccessToken accessToken = new AccessToken.Builder(acctSid, apiKey, apiSecret) + .identity(identity) + .grant(grant) + .build(); String token = accessToken.toJwt(); @@ -56,6 +58,19 @@ public static String createJsonAccessToken(String identity) { Gson gson = new Gson(); return gson.toJson(json); } + + private static boolean isPhoneNumber(String to) { + return to.matches("^[\\d\\+\\-\\(\\) ]+$"); + } + + private static Dial.Builder addChildReceiver(Dial.Builder builder, String to) { + // wrap the phone number or client name in the appropriate TwiML verb + // by checking if the number given has only digits and format symbols + if (Webapp.isPhoneNumber(to)) { + return builder.number(new Number.Builder(to).build()); + } + return builder.client(new Client.Builder(to).build()); + } public static String createVoiceResponse(String to) { VoiceResponse voiceTwimlResponse; @@ -64,16 +79,10 @@ public static String createVoiceResponse(String to) { Dial.Builder dialBuilder = new Dial.Builder() .callerId(System.getenv("TWILIO_CALLER_ID")); - // wrap the phone number or client name in the appropriate TwiML verb - // by checking if the number given has only digits and format symbols - if(to.matches("^[\\d\\+\\-\\(\\) ]+$")) { - dialBuilder = dialBuilder.number(new Number.Builder(to).build()); - } else { - dialBuilder = dialBuilder.client(new Client.Builder(to).build()); - } + Dial.Builder dialBuilderWithReceiver = Webapp.addChildReceiver(dialBuilder, to); voiceTwimlResponse = new VoiceResponse.Builder() - .dial(dialBuilder.build()) + .dial(dialBuilderWithReceiver.build()) .build(); } else { voiceTwimlResponse = new VoiceResponse.Builder()