-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DEVEXP-562: Add templates/server for GA APIs (#3)
- Loading branch information
1 parent
5169b10
commit 7e4ba94
Showing
13 changed files
with
499 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Express related configuration | ||
port = 3001 | ||
|
||
# The secret value used for webhook calls validation | ||
# See https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks/ | ||
NUMBERS_WEBHOOK_SECRET = <Your webhook secret configured for the Numbers API> | ||
|
||
# Application related credentials, used by: | ||
# - Verification | ||
# - Voice | ||
SINCH_APPLICATION_KEY = <Your Application Key> | ||
SINCH_APPLICATION_SECRET = <Your Application Secret> | ||
|
||
# Unified related credentials, used by the webhook business logic to send SMS batches. | ||
# US/EU are the only supported regions with unified credentials | ||
SINCH_PROJECT_ID = <Your Sinch Project ID> | ||
SINCH_KEY_ID = <Your Sinch Key ID> | ||
SINCH_KEY_SECRET = <Your Sinch Key Secret> | ||
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 (e.g.: 'au', 'br' or 'ca'). | ||
#SINCH_SERVICE_PLAN_ID = <Your Service Plan ID> | ||
#SINCH_API_TOKEN = <Your Service Plan Token> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# Backend application built using Sinch Node.js SDK to handle incoming webhooks | ||
|
||
This directory contains a server application based on the [Sinch Node.js SDK](https://github.com/sinch/sinch-sdk-node). | ||
|
||
## Requirements | ||
|
||
- [Node.js LTS](https://nodejs.org/en) | ||
- [Express](https://expressjs.com/) | ||
- [Sinch account](https://dashboard.sinch.com/) | ||
- [ngrok](https://ngrok.com/docs) | ||
|
||
## Configuration | ||
Edit the [.env](.env) file to set the parameters that will be used to configure the Express server and the controllers. | ||
|
||
### Server port | ||
*Default: 3001* | ||
- `port`: the port to be used to listen to incoming requests. Default is `3001` if not set. | ||
|
||
### Controller Configuration | ||
- Numbers controller: `NUMBERS_WEBHOOK_SECRET`. This value can be found thanks to the [Numbers API](https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks/), using the `/callbackConfiguration` endpoint. | ||
- SMS controller: no support for header validation at the moment | ||
- Verification and Voice controllers: the incoming `Authorization` header is signed with the same credentials as when using the API: | ||
- `SINCH_APPLICATION_KEY`=Your Application Key | ||
- `SINCH_APPLICATION_SECRET`=Your Application Secret | ||
|
||
## Usage | ||
|
||
### Starting the server | ||
|
||
1. Install the dependencies by running the command `npm install`. | ||
2. Edit the `.env` file with your own parameters (see the paragraph above for details). | ||
3. Run the code with one of the following commands: | ||
- `npm start` | ||
- `node src/server.js` | ||
|
||
### Endpoints | ||
|
||
When the server is up and running, the declared controllers will listen and respond to the following endpoints: | ||
|
||
| Service | Endpoint | | ||
|--------------|--------------------| | ||
| Numbers | /NumbersEvent | | ||
| SMS | /SmsEvent | | ||
| Verification | /VerificationEvent | | ||
| Voice | /VoiceEvent | | ||
|
||
## Use ngrok to forward the event request to the local server | ||
|
||
You can forward the request to your local server of the port it is listening to. | ||
|
||
*Note: The `3001` value is coming from the default configuration and can be changed (see [Server port](#Server port) configuration section)* | ||
|
||
```bash | ||
ngrok http 3001 | ||
``` | ||
|
||
The `ngrok` output will contain something like: | ||
``` | ||
ngrok (Ctrl+C to quit) | ||
... | ||
Forwarding https://cafe-64-220-29-200.ngrok-free.app -> http://localhost:3001 | ||
``` | ||
The line | ||
``` | ||
Forwarding https://cafe-64-220-29-200.ngrok-free.app -> http://localhost:3001 | ||
``` | ||
contains the "`https://cafe-64-220-29-200.ngrok-free.app`" value which will be used to determine the callback URLs, e.g.: | ||
- Numbers: https://cafe-64-220-29-200.ngrok-free.app/NumbersEvent | ||
- SMS: https://cafe-64-220-29-200.ngrok-free.app/SmsEvent | ||
- Verification: https://cafe-64-220-29-200.ngrok-free.app/VerificationEvent | ||
- Voice: https://cafe-64-220-29-200.ngrok-free.app/VoiceEvent | ||
|
||
This value must be used to configure the callback URLs: | ||
- Numbers: the callback URL must be set at Number level with the parameter `callbackUrl` when renting a number or when updating a rented number with the API. | ||
- SMS: the callback URL must be configured at Service level on the [Sinch dashboard](https://dashboard.sinch.com/sms/api/services) in the "Callback URLs" section. | ||
- Verification: the callback URL must be configured at Application level on the [Sinch dashboard](https://dashboard.sinch.com/verification/apps) in the "Settings/Callback URL" section. | ||
- Voice: the callback URL must be configured at Application level on the [Sinch dashboard](https://dashboard.sinch.com/voice/apps) in the "Settings/Handle call events with" section. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "@sinch/sdk-client-quickstart-template-server", | ||
"version": "0.0.0", | ||
"author": "Sinch", | ||
"description": "", | ||
"type": "module", | ||
"scripts": { | ||
"start": "node src/server.js", | ||
"compile": "tsc" | ||
}, | ||
"dependencies": { | ||
"@sinch/sdk-core": "^1.1.0", | ||
"dotenv": "^16.4.5", | ||
"express": "^4.20.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { NumbersCallbackWebhooks } from '@sinch/sdk-core'; | ||
import { handleNumbersEvent } from './serverBusinessLogic.js'; | ||
|
||
export const numbersController = (app) => { | ||
|
||
const callbackSecret = process.env.NUMBERS_WEBHOOK_SECRET || ''; | ||
const numbersCallbackWebhooks = new NumbersCallbackWebhooks(callbackSecret); | ||
|
||
app.post('/NumbersEvent', async (req, res) => { | ||
|
||
// Ensure the authentication is valid before processing the request | ||
const validAuth = numbersCallbackWebhooks.validateAuthenticationHeader( | ||
req.headers, | ||
req.rawBody, | ||
); | ||
|
||
// Authentication header failed | ||
if (!validAuth) { | ||
return res.status(401).json(); | ||
} | ||
|
||
// Parse the request payload | ||
const event = numbersCallbackWebhooks.parseEvent(req.body); | ||
|
||
// Let the business layer process the request | ||
await handleNumbersEvent(event); | ||
|
||
return res.status(200).json(); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* @typedef {import("@sinch/sdk-core").NumbersCallback} NumbersCallback | ||
*/ | ||
|
||
/** | ||
* Handles a Numbers Event. | ||
* @param {NumbersCallback} numbersEvent - The incoming Numbers event notification. | ||
*/ | ||
export const handleNumbersEvent = async (numbersEvent) => { | ||
console.log(`Handling Numbers event:\n${JSON.stringify(numbersEvent, null, 2)}`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import express from 'express'; | ||
import { numbersController } from './numbers/controller.js'; | ||
import { smsController } from './sms/controller.js'; | ||
import { verificationController } from './verification/controller.js'; | ||
import { voiceController } from './voice/controller.js'; | ||
import * as dotenv from 'dotenv'; | ||
dotenv.config(); | ||
|
||
const app = express(); | ||
const port = process.env.port || 3001; | ||
|
||
// Middleware to capture the raw body and store it in req.rawBody | ||
app.use((req, res, next) => { | ||
let data = ''; | ||
|
||
req.on('data', (chunk) => { | ||
data += chunk; | ||
}); | ||
|
||
req.on('end', () => { | ||
req.rawBody = data; | ||
req.body = JSON.parse(data); | ||
next(); | ||
}); | ||
|
||
req.on('error', (err) => { | ||
next(err); | ||
}); | ||
}); | ||
|
||
numbersController(app); | ||
smsController(app); | ||
verificationController(app); | ||
voiceController(app); | ||
|
||
app.listen(port, () => { | ||
console.log(`Server is listening on port ${port}`); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { SmsCallbackWebhooks } from '@sinch/sdk-core'; | ||
import { | ||
handleIncomingBinaryMessageEvent, | ||
// TODO: uncomment with the version 1.2.0 | ||
// handleIncomingMmsEvent, | ||
handleIncomingSmsEvent, | ||
handleMmsDeliveryReportEvent, | ||
handleMmsRecipientDeliveryReportEvent, | ||
handleSmsDeliveryReportEvent, | ||
handleSmsRecipientDeliveryReportEvent, | ||
} from './serverBusinessLogic.js'; | ||
|
||
export const smsController = (app) => { | ||
|
||
const smsCallbackWebhooks = new SmsCallbackWebhooks(); | ||
|
||
app.post('/SmsEvent', async (req, res) => { | ||
|
||
// Parse the request payload | ||
const event = smsCallbackWebhooks.parseEvent(req.body); | ||
|
||
// Let the business layer process the request | ||
switch (event.type) { | ||
case 'mo_text': | ||
handleIncomingSmsEvent(event); | ||
break; | ||
case 'mo_binary': | ||
handleIncomingBinaryMessageEvent(event); | ||
break; | ||
// TODO: uncomment with the version 1.2.0 | ||
// case 'mo_media': | ||
// handleIncomingMmsEvent(event); | ||
// break; | ||
case 'delivery_report_sms': | ||
handleSmsDeliveryReportEvent(event); | ||
break; | ||
case 'delivery_report_mms': | ||
handleMmsDeliveryReportEvent(event); | ||
break; | ||
case 'recipient_delivery_report_sms': | ||
handleSmsRecipientDeliveryReportEvent(event); | ||
break; | ||
case 'recipient_delivery_report_mms': | ||
handleMmsRecipientDeliveryReportEvent(event); | ||
break; | ||
default: | ||
res.status(500).json({ error: 'Unsupported event type' }); | ||
} | ||
|
||
return res.status(200).json(); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// eslint-disable-next-line no-unused-vars | ||
import { Sms } from '@sinch/sdk-core'; | ||
|
||
/** | ||
* Handles an incoming SMS Event. | ||
* @param {Sms.MOText} incomingSmsEvent - The incoming SMS event. | ||
*/ | ||
export const handleIncomingSmsEvent = (incomingSmsEvent) => { | ||
console.log(`Handling incoming SMS event:\n${JSON.stringify(incomingSmsEvent, null, 2)}`); | ||
}; | ||
|
||
/** | ||
* Handles an incoming binary message Event. | ||
* @param {Sms.MOBinary} incomingBinaryMessageEvent - The incoming binary message event. | ||
*/ | ||
export const handleIncomingBinaryMessageEvent = (incomingBinaryMessageEvent) => { | ||
console.log(`Handling incoming binary message event:\n${JSON.stringify(incomingBinaryMessageEvent, null, 2)}`); | ||
}; | ||
|
||
// TODO: uncomment with the version 1.2.0 | ||
// /** | ||
// * Handles an incoming MMS Event. | ||
// * @param {Sms.MOMedia} incomingMmsEvent - The incoming MMS event. | ||
// */ | ||
// export const handleIncomingMmsEvent = (incomingMmsEvent) => { | ||
// console.log(`Handling incoming MMS event:\n${JSON.stringify(incomingMmsEvent, null, 2)}`); | ||
// }; | ||
|
||
/** | ||
* Handles an SMS Delivery Report Event. | ||
* @param {Sms.DeliveryReport} smsDeliveryReportEvent - The SMS delivery report event. | ||
*/ | ||
export const handleSmsDeliveryReportEvent = (smsDeliveryReportEvent) => { | ||
console.log(`Handling SMS delivery report event:\n${JSON.stringify(smsDeliveryReportEvent, null, 2)}`); | ||
}; | ||
|
||
/** | ||
* Handles an MMS Delivery Report Event. | ||
* @param {Sms.DeliveryReport} mmsDeliveryReportEvent - The MMS delivery report event. | ||
*/ | ||
export const handleMmsDeliveryReportEvent = (mmsDeliveryReportEvent) => { | ||
console.log(`Handling MMS delivery report event:\n${JSON.stringify(mmsDeliveryReportEvent, null, 2)}`); | ||
}; | ||
|
||
/** | ||
* Handles an SMS Recipient Delivery Report Event. | ||
* @param {Sms.RecipientDeliveryReport} smsRecipientDeliveryReportEvent - The SMS recipient delivery report event. | ||
*/ | ||
export const handleSmsRecipientDeliveryReportEvent = (smsRecipientDeliveryReportEvent) => { | ||
console.log(`Handling SMS recipient delivery report event:\n${JSON.stringify(smsRecipientDeliveryReportEvent, null, 2)}`); | ||
}; | ||
|
||
/** | ||
* Handles an MMS Recipient Delivery Report Event. | ||
* @param {Sms.RecipientDeliveryReport} mmsRecipientDeliveryReportEvent - The MMS recipient delivery report event. | ||
*/ | ||
export const handleMmsRecipientDeliveryReportEvent = (mmsRecipientDeliveryReportEvent) => { | ||
console.log(`Handling MMS recipient delivery report event:\n${JSON.stringify(mmsRecipientDeliveryReportEvent, null, 2)}`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { VerificationCallbackWebhooks } from '@sinch/sdk-core'; | ||
import { handleVerificationRequestEvent, handleVerificationResultEvent } from './serverBusinessLogic.js'; | ||
|
||
export const verificationController = (app) => { | ||
|
||
const applicationKey = process.env.SINCH_APPLICATION_KEY; | ||
const applicationSecret = process.env.SINCH_APPLICATION_SECRET; | ||
|
||
const verificationCallbackWebhooks = new VerificationCallbackWebhooks({ | ||
applicationKey, | ||
applicationSecret, | ||
}); | ||
|
||
app.post('/VerificationEvent', async (req, res) => { | ||
|
||
// Ensure the authentication is valid before processing the request | ||
const validAuth = verificationCallbackWebhooks.validateAuthenticationHeader( | ||
req.headers, | ||
req.rawBody, | ||
'/VerificationEvent', | ||
'POST', | ||
); | ||
|
||
// Authentication header failed | ||
if (!validAuth) { | ||
return res.status(401).json(); | ||
} | ||
|
||
// Parse the request payload | ||
const event = verificationCallbackWebhooks.parseEvent(req.body); | ||
|
||
// Let the business layer process the request | ||
let response; | ||
switch (event.event) { | ||
case 'VerificationRequestEvent': | ||
response = handleVerificationRequestEvent(event); | ||
break; | ||
case 'VerificationResultEvent': | ||
response = handleVerificationResultEvent(event); | ||
break; | ||
default: | ||
res.status(500).json({ error: 'Unsupported event type' }); | ||
} | ||
|
||
console.log(`JSON response:\n${JSON.stringify(response, null, 2)}`); | ||
|
||
return res.status(200).json(response); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// eslint-disable-next-line no-unused-vars | ||
import { Verification } from '@sinch/sdk-core'; | ||
|
||
/** | ||
* Handles a Verification Request Event. | ||
* @param {Verification.VerificationRequestEvent} verificationRequestEvent - The incoming Verification Request event. | ||
* @return {Verification.VerificationRequestEventResponse} the verification request event response | ||
*/ | ||
export const handleVerificationRequestEvent = (verificationRequestEvent) => { | ||
console.log(`Handling Verification Request event:\n${JSON.stringify(verificationRequestEvent, null, 2)}`); | ||
|
||
// add your logic here according to SMS, FlashCall, PhoneCall, ... verification | ||
return {}; | ||
}; | ||
|
||
/** | ||
* Handles a Verification Result Event. | ||
* @param {Verification.VerificationResultEvent} verificationResultEvent - The incoming Verification Result notification. | ||
* @return {null} | ||
*/ | ||
export const handleVerificationResultEvent = (verificationResultEvent) => { | ||
console.log(`Handling Verification Result event:\n${JSON.stringify(verificationResultEvent, null, 2)}`); | ||
return null; | ||
}; |
Oops, something went wrong.