diff --git a/README.md b/README.md
index 41d598bf..f784ac70 100644
--- a/README.md
+++ b/README.md
@@ -138,14 +138,14 @@ console.log(`The SMS has been sent successfully. Here is the batch id: ${respons
Here is the list of the Sinch products and their level of support by the Node.js SDK:
-| API Category | API Name | Status |
-|------------------------|-------------------------------------|:------:|
-| Messaging | SMS API | ✅ |
-| | Conversation API | ✅ |
-| | Fax API | 🚧 |
-| Voice and Video | Voice API | ✅ |
-| Numbers & Connectivity | Numbers API | ✅ |
-| Verification | Verification API | ✅ |
+| API Category | API Name | Status |
+|------------------------|------------------|:------:|
+| Messaging | SMS API | ✅ |
+| | Conversation API | ✅ |
+| | Fax API | ✅ |
+| Voice and Video | Voice API | ✅ |
+| Numbers & Connectivity | Numbers API | ✅ |
+| Verification | Verification API | ✅ |
### Packages
diff --git a/packages/conversation/src/rest/v1/conversation-service.ts b/packages/conversation/src/rest/v1/conversation-service.ts
index 1815f0ed..038ae620 100644
--- a/packages/conversation/src/rest/v1/conversation-service.ts
+++ b/packages/conversation/src/rest/v1/conversation-service.ts
@@ -1,4 +1,4 @@
-import { SinchClientParameters } from '@sinch/sdk-client';
+import { ConversationRegion, SinchClientParameters } from '@sinch/sdk-client';
import { ContactApi } from './contact';
import { AppApi } from './app';
import { EventsApi } from './events';
@@ -10,6 +10,19 @@ import { WebhooksApi } from './webhooks';
import { TemplatesV1Api } from './templates-v1';
import { TemplatesV2Api } from './templates-v2';
+/**
+ * The Conversation Service exposes the following APIs:
+ * - app
+ * - contact
+ * - capability
+ * - conversation
+ * - messages
+ * - events
+ * - transcoding
+ * - webhooks
+ * - templatesV1
+ * - templatesV2
+ */
export class ConversationService {
public readonly contact: ContactApi;
public readonly app: AppApi;
@@ -22,7 +35,18 @@ export class ConversationService {
public readonly templatesV1: TemplatesV1Api;
public readonly templatesV2: TemplatesV2Api;
-
+ /**
+ * Create a new ConversationService instance with its configuration. It needs the following parameters for authentication:
+ * - `projectId`
+ * - `keyId`
+ * - `keySecret`
+ *
+ * Other supported properties:
+ * - `conversationRegion`
+ * - `conversationHostname`
+ * - `conversationTemplatesHostname`
+ * @param {SinchClientParameters} params - an Object containing the necessary properties to initialize the service
+ */
constructor(params: SinchClientParameters) {
this.contact = new ContactApi(params);
this.app = new AppApi(params);
@@ -52,11 +76,28 @@ export class ConversationService {
}
/**
- * Update the default hostname for the Templates API
+ * Update the default hostname for the Templates APIs
* @param {string} hostname - The new hostname to use for the Templates APIs.
*/
public setTemplatesHostname(hostname: string) {
this.templatesV1.setHostname(hostname);
this.templatesV2.setHostname(hostname);
}
+
+ /**
+ * Update the current region for each API
+ * @param {ConversationRegion} region - The new region to use in the production URL
+ */
+ public setRegion(region: ConversationRegion) {
+ this.contact.setRegion(region);
+ this.app.setRegion(region);
+ this.events.setRegion(region);
+ this.messages.setRegion(region);
+ this.transcoding.setRegion(region);
+ this.capability.setRegion(region);
+ this.conversation.setRegion(region);
+ this.webhooks.setRegion(region);
+ this.templatesV1.setRegion(region);
+ this.templatesV2.setRegion(region);
+ }
}
diff --git a/packages/conversation/tests/rest/v1/conversation-service.test.ts b/packages/conversation/tests/rest/v1/conversation-service.test.ts
index 6f678036..2470537d 100644
--- a/packages/conversation/tests/rest/v1/conversation-service.test.ts
+++ b/packages/conversation/tests/rest/v1/conversation-service.test.ts
@@ -1,4 +1,4 @@
-import { SinchClientParameters } from '@sinch/sdk-client';
+import { ConversationRegion, SinchClientParameters } from '@sinch/sdk-client';
import {
AppApi,
CapabilityApi,
@@ -15,8 +15,10 @@ import {
describe('Conversation Service', () => {
const DEFAULT_HOSTNAME = 'https://us.conversation.api.sinch.com';
+ const EUROPE_HOSTNAME = 'https://eu.conversation.api.sinch.com';
const CUSTOM_HOSTNAME = 'https://new.host.name';
const DEFAULT_HOSTNAME_TEMPLATES = 'https://us.template.api.sinch.com';
+ const EUROPE_HOSTNAME_TEMPLATES = 'https://eu.template.api.sinch.com';
const CUSTOM_HOSTNAME_TEMPLATES = 'https://templates.new.host.name';
it('should initialize all the APIs', () => {
@@ -92,4 +94,29 @@ describe('Conversation Service', () => {
expect(conversationService.templatesV1.getSinchClient().apiClientOptions.hostname).toBe(CUSTOM_HOSTNAME_TEMPLATES);
expect(conversationService.templatesV2.getSinchClient().apiClientOptions.hostname).toBe(CUSTOM_HOSTNAME_TEMPLATES);
});
+
+ it('should update the default region for all APIs', () => {
+ // Given
+ const params: SinchClientParameters = {
+ projectId: 'PROJECT_ID',
+ keyId: 'KEY_ID',
+ keySecret: 'KEY_SECRET',
+ };
+ const conversationService = new ConversationService(params);
+
+ // When
+ conversationService.setRegion(ConversationRegion.EUROPE);
+
+ // Then
+ expect(conversationService.contact.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.app.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.events.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.messages.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.transcoding.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.capability.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.conversation.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.webhooks.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(conversationService.templatesV1.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME_TEMPLATES);
+ expect(conversationService.templatesV2.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME_TEMPLATES);
+ });
});
diff --git a/packages/fax/src/rest/v3/fax-service.ts b/packages/fax/src/rest/v3/fax-service.ts
index 873debc5..027de894 100644
--- a/packages/fax/src/rest/v3/fax-service.ts
+++ b/packages/fax/src/rest/v3/fax-service.ts
@@ -1,13 +1,30 @@
-import { SinchClientParameters } from '@sinch/sdk-client';
+import { FaxRegion, SinchClientParameters } from '@sinch/sdk-client';
import { EmailsApi } from './emails';
import { FaxesApi } from './faxes';
import { ServicesApi } from './services';
+/**
+ * The Fax Service exposes the following APIs:
+ * - services
+ * - faxes
+ * - emails
+ */
export class FaxService {
public readonly emails: EmailsApi;
public readonly faxes: FaxesApi;
public readonly services: ServicesApi;
+ /**
+ * Create a new FaxService instance with its configuration. It needs the following parameters for authentication:
+ * - `projectId`
+ * - `keyId`
+ * - `keySecret`
+ *
+ * Other supported properties:
+ * - `faxRegion`
+ * - `faxHostname`
+ * @param {SinchClientParameters} params - an Object containing the necessary properties to initialize the service
+ */
constructor(params: SinchClientParameters) {
this.emails = new EmailsApi(params);
this.faxes = new FaxesApi(params);
@@ -16,7 +33,6 @@ export class FaxService {
/**
* Update the default hostname for each API
- *
* @param {string} hostname - The new hostname to use for all the APIs.
*/
public setHostname(hostname: string) {
@@ -24,4 +40,14 @@ export class FaxService {
this.faxes.setHostname(hostname);
this.services.setHostname(hostname);
}
+
+ /**
+ * Update the current region for each API
+ * @param {FaxRegion} region - The new region to use in the production URL
+ */
+ public setRegion(region: FaxRegion) {
+ this.emails.setRegion(region);
+ this.faxes.setRegion(region);
+ this.services.setRegion(region);
+ }
}
diff --git a/packages/numbers/src/rest/v1/numbers-service.ts b/packages/numbers/src/rest/v1/numbers-service.ts
index be1a7654..cc6002af 100644
--- a/packages/numbers/src/rest/v1/numbers-service.ts
+++ b/packages/numbers/src/rest/v1/numbers-service.ts
@@ -4,12 +4,29 @@ import { CallbacksApi } from './callbacks';
import { AvailableNumberApi } from './available-number';
import { ActiveNumberApi } from './active-number';
+/**
+ * The Numbers Service exposes the following APIs:
+ * - availableRegions
+ * - availableNumber
+ * - activeNumber
+ * - callbacks
+ */
export class NumbersService {
public readonly availableRegions: AvailableRegionsApi;
public readonly callbacks: CallbacksApi;
public readonly availableNumber: AvailableNumberApi;
public readonly activeNumber: ActiveNumberApi;
+ /**
+ * Create a new NumbersService instance with its configuration. It needs the following parameters for authentication:
+ * - `projectId`
+ * - `keyId`
+ * - `keySecret`
+ *
+ * Other supported properties:
+ * - `numbersHostname`
+ * @param {SinchClientParameters} params - an Object containing the necessary properties to initialize the service
+ */
constructor(params: SinchClientParameters) {
this.availableRegions = new AvailableRegionsApi(params);
this.callbacks = new CallbacksApi(params);
diff --git a/packages/sdk-client/README.md b/packages/sdk-client/README.md
index 2fe6ec02..b9d7014e 100644
--- a/packages/sdk-client/README.md
+++ b/packages/sdk-client/README.md
@@ -1,17 +1,23 @@
# Sinch Http Client for Node.js
-This package contains the HTTP client used by the Sinch SDK client. It uses the `fetch` library to send requests. There are 3 folders in this package:
- - api: it defines the interface for an `Api`, an `ApiClient` and for `ApiClientOptions`.
- - client: it contains the `fetch` implementation for the `ApiClient`. This implementation handles the refresh of the JWT for the APIs which support this authentication method.
- - plugins: calling an API can be seen as a straighforward operation as it's "just" about sending an HTTP request to a server and reading the response. And this is was the `ApiClient` does. However, all APIs don't behave the same and this is why we have introduced the request and response plugins:
+This package contains the HTTP client used by the Sinch SDK client. It uses 2 third-party dependencies:
+ - `node-fetch` to send requests.
+ - `form-data` to manage "multipart/form-data" streams.
+
+There are 5 folders in this package:
+ - `api`: it defines the interface for an `Api`, an `ApiClient` and for `ApiClientOptions`.
+ - `client`: it contains the `fetch` implementation for the `ApiClient`. This implementation handles the refresh of the JWT for the APIs which support this authentication method.
+ - `domain`: it contains some Sinch-specific interfaces and enums used at domain level for authentication and region definition.
+ - `plugins`: calling an API can be seen as a straighforward operation as it's "just" about sending an HTTP request to a server and reading the response. And this is was the `ApiClient` does. However, all APIs don't behave the same and this is why we have introduced the request and response plugins:
- request plugins will modify the request: for the moment, they are all about adding some headers (for authentication, for tracking the SDK usage).
- response plugins will modify the response, e.g.: response content is checked and transformed into an exception if the content is empty.
+ - `utils`: it contains some utility methods that are used by the API SDKs, such as the methods to validate headers are valid in a callback event request, or a method to convert text to hexadecimal for a UDH header
> Warning:
> **This SDK is currently available to selected developers for preview use only. It is being provided for the purpose of collecting feedback, and should not be used in production environments.**
## Architecture
-TODO: schema
+![High level architecture](./documentation/High-level-architecture.png)
## Installation
diff --git a/packages/sdk-client/documentation/High-level-architecture.png b/packages/sdk-client/documentation/High-level-architecture.png
new file mode 100644
index 00000000..9835d82b
Binary files /dev/null and b/packages/sdk-client/documentation/High-level-architecture.png differ
diff --git a/packages/sdk-client/src/domain/domain-interface.ts b/packages/sdk-client/src/domain/domain-interface.ts
index 35d68f4e..bfb7d186 100644
--- a/packages/sdk-client/src/domain/domain-interface.ts
+++ b/packages/sdk-client/src/domain/domain-interface.ts
@@ -1,6 +1,13 @@
import { RequestPlugin } from '../plugins/core/request-plugin';
import { ResponsePlugin } from '../plugins/core/response-plugin';
+/**
+ * Global object that holds the API configuration.
+ * Be careful to follow the guidelines defined by the API Services about which parameters are required for each API. Not all of them use the same authentication mechanism:
+ * - OAuth2: Conversation, Fax, Numbers and SMS (US and EU regions only)
+ * - API Token: SMS on all regions
+ * - Application Signed: Verification and Voice
+ */
export interface SinchClientParameters extends
Partial,
Partial,
@@ -46,19 +53,30 @@ export interface ApplicationCredentials {
}
export interface ApiHostname {
+ /** Override the hostname for the OAuth2 authentication API */
authHostname?: string;
+ /** Override the hostname for the Conversation API (not Conversation Templates) - Note the regions become ineffective */
conversationHostname?: string;
+ /** Override the hostname for the Conversation Templates API - Note the regions become ineffective */
conversationTemplatesHostname?: string;
+ /** Override the hostname for the Fax API - Note the regions become ineffective */
faxHostname?: string;
+ /** Override the hostname for the Numbers API */
numbersHostname?: string;
+ /** Override the hostname for the SMS API - Note the regions become ineffective */
smsHostname?: string;
+ /** Override the hostname for the Verification API */
verificationHostname?: string;
+ /** Override the hostname for the Voice API (not Voice Application Management) - Note the regions become ineffective */
voiceHostname?: string;
+ /** Override the hostname for the Voice Application Management API */
voiceApplicationManagementHostname?: string;
}
export interface ApiPlugins {
+ /** Add more plugins to action on the request before it is sent */
requestPlugins?: RequestPlugin[];
+ /** Add more plugins to action on the server response before it is returned in the Promise */
responsePlugins?: ResponsePlugin[];
}
diff --git a/packages/sdk-client/src/plugins/additional-headers/additional-headers.request.ts b/packages/sdk-client/src/plugins/additional-headers/additional-headers.request.ts
index 7868597e..08fc149c 100644
--- a/packages/sdk-client/src/plugins/additional-headers/additional-headers.request.ts
+++ b/packages/sdk-client/src/plugins/additional-headers/additional-headers.request.ts
@@ -5,6 +5,15 @@ export interface AdditionalHeaders {
headers: Promise<{ [key: string]: string }>;
}
+/**
+ * Builds a Promise containing the new header in the form of an object {key : value}
+ * @param {string} key - the header key
+ * @param {string} value - the header value
+ */
+export const buildHeader = async (key: string, value: string): Promise<{[key: string]: string}> => (
+ { [key] : value }
+);
+
export class AdditionalHeadersRequest implements RequestPlugin {
private readonly additionalHeaders: AdditionalHeaders;
diff --git a/packages/sdk-client/src/plugins/index.ts b/packages/sdk-client/src/plugins/index.ts
index f12573de..caf2ec7d 100644
--- a/packages/sdk-client/src/plugins/index.ts
+++ b/packages/sdk-client/src/plugins/index.ts
@@ -1,4 +1,5 @@
export * from './core';
+export * from './additional-headers';
export * from './api-token';
export * from './basicAuthentication';
export * from './exception';
diff --git a/packages/sdk-core/README.md b/packages/sdk-core/README.md
index 8149b862..abdb2b9a 100644
--- a/packages/sdk-core/README.md
+++ b/packages/sdk-core/README.md
@@ -30,17 +30,19 @@ As there are different authentication schemes, the initialization method will de
```typescript
import { SinchClient } from '@sinch/sdk-core';
-// The credentials can be found on the Account dashboard: https://dashboard.sinch.com/account/access-keys
-const sinch: Pick = new SinchClient({
+(async () => {
+ // The credentials can be found on the Account dashboard: https://dashboard.sinch.com/account/access-keys
+ const sinch: Pick = new SinchClient({
projectId: 'my-project-id',
keyId: 'my-key-id',
keySecret: 'my-key-secret',
-});
-const numbersService = sinch.numbers;
-
-const response = await numbersService.availableRegions.list({
- types: 'LOCAL',
-});
+ });
+ const numbersService = sinch.numbers;
+
+ const response = await numbersService.availableRegions.list({
+ types: ['LOCAL'],
+ });
+})();
```
The initialization method above will work for the APIs that supports the authentication with OAuth2 (Numbers and SMS on US and EU regions).
@@ -51,17 +53,19 @@ If you want to use the SMS API on the other regions (or US and EU too, it will w
```typescript
import { SinchClient } from '@sinch/sdk-core';
-// The credentials can be found on the Service APIs dashboard: https://dashboard.sinch.com/sms/api/services
-const sinch: Pick = new SinchClient({
+(async () => {
+ // The credentials can be found on the Service APIs dashboard: https://dashboard.sinch.com/sms/api/services
+ const sinch: Pick = new SinchClient({
servicePlanId: 'my-service-plan-id',
apiToken: 'my-key-id',
- region: 'my-region', // Optional, can be 'us', 'eu', 'br', 'au', 'ca'. Default is 'us'
-});
-const smsService = sinch.sms;
-
-const response = await smsService.batches.get({
+ smsRegion: 'my-region', // Optional, can be 'us', 'eu', 'br', 'au', 'ca'. Default is 'us'
+ });
+ const smsService = sinch.sms;
+
+ const response = await smsService.batches.get({
batch_id: '01HF28S9AABBBCCCCY92BJB569',
-});
+ });
+})();
```
### Signed application authentication
@@ -71,18 +75,254 @@ Both `Verification` and `Voice` APIs are using this authentication method: the r
```typescript
import { SinchClient } from '@sinch/sdk-core';
-// The credentials can be found on the Verification or Voice dashboard:
-// https://dashboard.sinch.com/verification/apps or
-// https://dashboard.sinch.com/voice/apps
-const sinch: Pick = new SinchClient({
- applicationId: 'my-application-key',
- applicationSecret: 'my-application-secret',
+(async () => {
+ // The credentials can be found on the Verification or Voice dashboard:
+ // https://dashboard.sinch.com/verification/apps or
+ // https://dashboard.sinch.com/voice/apps
+ const sinch: Pick = new SinchClient({
+ applicationId: 'my-application-key',
+ applicationSecret: 'my-application-secret',
+ });
+ const verificationService = sinch.verification;
+
+ const response = await verificationService.verificationStatus.getById({
+ id: '018bfc3e-1234-5678-1234-ebdb3fd6d30f',
+ });
+})();
+```
+
+## Importing classes and interfaces from other API packages
+
+For convenience, importing `@sinch/sdk-core` is sufficient to be able to access to all the APIs' classes and interfaces. In case you need to use a single API, they are also packaged as single NPM packages:
+ - SMS: [`@sinch/sms`](https://www.npmjs.com/package/@sinch/sms)
+ - Conversation: [`@sinch/conversation`](https://www.npmjs.com/package/@sinch/conversation)
+ - Fax: [`@sinch/fax`](https://www.npmjs.com/package/@sinch/fax)
+ - Numbers: [`@sinch/numbers`](https://www.npmjs.com/package/@sinch/numbers)
+ - Verification: [`@sinch/verification`](https://www.npmjs.com/package/@sinch/verification)
+ - Voice: [`@sinch/voice`](https://www.npmjs.com/package/@sinch/voice)
+
+All the interfaces are exported with an alias, equal to the API name: `Sms` for the SMS API, `Conversation` for the Conversation API, `Fax` for the Fax API and so on.
+
+Here is an example about using the TypeScript types to send a fax:
+
+```typescript
+import {
+ Fax, // "Fax" is the alias under which are exported all the interfaces
+ FaxService, // This is a class so it is exported as is
+ SinchClient,
+} from '@sinch/sdk-core';
+
+(async () => {
+ const sinch = new SinchClient({
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
+ });
+
+ const faxService: FaxService = sinch.fax;
+
+ const requestData: Fax.SendFaxRequestData = {
+ sendFaxRequestBody: {
+ to: 'a valid destination number',
+ from: 'a valid origin number',
+ contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
+ },
+ };
+
+ const response: Fax.Fax = await faxService.faxes.send(requestData);
+})();
+```
+
+## Importing classes for CommonJS
+
+Until now, all the examples in this documentation are showcasing ES modules but there may be some cases when the SDK user will want to use CommonJS instead.
+
+Here is an example to do the same as above with CommonJS (using `require`):
+```javascript
+const { SinchClient } = require('@sinch/sdk-core');
+
+(async () => {
+ const sinch = new SinchClient({
+ projectId: 'YOUR_project_id',
+ keyId: 'YOUR_access_key',
+ keySecret: 'YOUR_access_secret',
+ });
+
+ const faxService = sinch.fax;
+
+ const requestData = {
+ sendFaxRequestBody: {
+ to: 'a valid destination number',
+ from: 'a valid origin number',
+ contentUrl: 'https://developers.sinch.com/fax/fax.pdf',
+ },
+ };
+
+ const response = await faxService.faxes.send(requestData);
+})();
+```
+
+## SinchClient parameters override
+
+### Hostname and region override
+
+For various reasons (development phase, testing, network restrictions, ...), one may need to update the default API endpoint, pointing to production.
+Each API exposes dedicated parameters to override the default hostname and region:
+
+| API Name | Parameter | Region |
+|----------------|------------------------------------|--------------------|
+| Authentication | authHostname | N/A |
+| SMS | smsHostname | smsRegion |
+| Conversation | conversationHostname | conversationRegion |
+| | conversationTemplatesHostname | |
+| Fax | faxHostname | faxRegion |
+| Voice | voiceHostname | voiceRegion |
+| | voiceApplicationManagementHostname | |
+| Numbers | numbersHostname | N/A |
+| Verification | verificationHostname | N/A |
+
+And here are the list of supported regions per regionalized API:
+
+| Api Name | Supported regions |
+|--------------|---------------------------------------------------------------|
+| SMS | SmsRegion.UNITED_STATES |
+| | SmsRegion.EUROPE |
+| | SmsRegion.BRAZIL (not available for OAuth2 authentication) |
+| | SmsRegion.CANADA (not available for OAuth2 authentication) |
+| | SmsRegion.AUSTRALIA (not available for OAuth2 authentication) |
+| Conversation | ConversationRegion.UNITED_STATES |
+| | ConversationRegion.EUROPE |
+| | ConversationRegion.BRAZIL |
+| Fax | FaxRegion.DEFAULT |
+| | FaxRegion.UNITED_STATES |
+| | FaxRegion.EUROPE |
+| | FaxRegion.SOUTH_AMERICA |
+| | FaxRegion.SOUTHEAST_ASIA_1 |
+| | FaxRegion.SOUTHEAST_ASIA_2 |
+| Voice | VoiceRegion.DEFAULT |
+| | VoiceRegion.UNITED_STATES |
+| | VoiceRegion.EUROPE |
+| | VoiceRegion.SOUTH_AMERICA |
+| | VoiceRegion.SOUTHEAST_ASIA_1 |
+| | VoiceRegion.SOUTHEAST_ASIA_2 |
+
+**Use case #1:** overriding the region in production
+
+```typescript
+import { SinchClientParameters } from '@sinch/sdk-core';
+
+const conversationClientParametersEurope: SinchClientParameters = {
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
+ conversationRegion: ConversationRegion.EUROPE,
+};
+```
+**Use case #2:** overriding the hostname
+```typescript
+import { SinchClientParameters } from '@sinch/sdk-core';
+
+const conversationClientParametersForTest: SinchClientParameters = {
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
+ authHostname: 'http://my-test-server:3000',
+ conversationHostname: 'http://my-test-server:3001',
+ conversationTemplatesHostname: 'http://my-test-server:3002',
+};
+```
+**Note**: when overriding the hostname, the `region` parameter becomes obsolete and is not taken into account when sending the request.
+
+**Note**: The region parameter is permissive: instead of the pre-defined list, you can also set any `string`. This covers the case where a new region is added (in production or in preview) and the SDK is not yet ready to support it or the SDK user doesn't want to update its SDK version to benefit from it.
+```typescript
+import { SinchClientParameters } from '@sinch/sdk-core';
+
+const conversationClientParametersNewRegion: SinchClientParameters = {
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
+ conversationRegion: 'bzh',
+};
+```
+
+On top of that, once you have instantiated an API service, it is possible to override the hostname and the region (in case the API endpoint is regionalized). The services expose the following methods:
+ - `setHostname()`
+ - `setRegion()`
+
+These methods can be used at service level (all the APIs will be updated) or at API domain level (for updating only one at a time):
+
+**Use case #1:** overriding the hostname per API domain
+
+```typescript
+import { SinchClient } from '@sinch/sdk-core';
+
+const sinch: SinchClient = new SinchClient({
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
+});
+const numbersService = sinch.numbers;
+numbersService.setHostname('https://my.new.server');
+numbersService.activeNumber.setHostname('https://my.other.server');
+// Result:
+// numbers.availableRegions -> https://my.new.server
+// numbers.availableNumber -> https://my.new.server
+// numbers.activeNumber -> https://my.other.server
+// numbers.callbacks -> https://my.new.server
+```
+
+**Use case #2:** overriding the production region per API domain
+import { SinchClient } from '@sinch/sdk-core';
+
+```typescript
+import { SinchClient, VoiceRegion } from '@sinch/sdk-core';
+
+const sinch: SinchClient = new SinchClient({
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
});
-const verificationService = sinch.verification;
+const voiceService = sinch.voice;
+voiceService.setRegion(VoiceRegion.SOUTH_AMERICA);
+voiceService.calls.setRegion(VoiceRegion.SOUTHEAST_ASIA_2);
+// Result:
+// voice.applications -> https://callingapi.sinch.com (This one is not regionalized !)
+// voice.conferences -> https://calling-sae1.api.sinch.com
+// voice.calls -> https://calling-apse2.api.sinch.com
+// voice.callouts -> https://calling-sae1.api.sinch.com
+```
+
+### Request and response plugins addition
+
+When instantiating a service, it creates an ApiClient behind the scene (find more in the [@sinch/sdk-client](../sdk-client/README.md) package) which contains some default plugins:
+ - request plugins: `VersionRequest` which will add a `user-agent` with the used version of the SDK and Node.js running the program
+ - response plugins:
+ - `TimezonePlugin` which will ensure that all the dates returned by the server contain a timezone
+ - `ExceptionPlugin` which will catch all invalid response from the server and format them in a common way to handle exceptions
+
+On top of that, the Service will add its own plugins:
+ - for OAuth2 authentication: `Oauth2TokenRequest`
+ - for API Token authentication: `ApiTokenRequest`
+ - for Application Signed authentication: `XTimestampRequest` and `SigningRequest`
+
+And on top on these plugins, it is possible to add even more of them (existing ones or custom ones) with the following properties of the `SinchClientParameters`:
+ - `requestPlugins`
+ - `responsePlugins`
+
+```typescript
+import { SinchClient, AdditionalHeadersRequest, buildHeader } from '@sinch/sdk-core';
-const response = await verificationService.verificationStatus.getById({
- id: '018bfc3e-1234-5678-1234-ebdb3fd6d30f',
+const sinch: SinchClient = new SinchClient({
+ projectId: 'my-project-id',
+ keyId: 'my-key-id',
+ keySecret: 'my-key-secret',
+ requestPlugins: [
+ new AdditionalHeadersRequest({
+ headers: buildHeader('customHeader', 'customHeaderValue'),
+ }),
+ ],
});
+// => All the requests using services registered on this SinchClient will send an extra-header
```
## Examples
@@ -95,11 +335,11 @@ Developer Experience team: [devexp@sinch.com](mailto:devexp@sinch.com)
Here is the list of the Sinch API and there level of support by the Node.js SDK:
-| API Category | API Name | Status |
-|------------------------|-------------------------------------|:------:|
-| Messaging | SMS API | ✅ |
-| | Conversation API | ✅ |
-| | Fax API | 🚧 |
-| Voice and Video | Voice API | ✅ |
-| Numbers & Connectivity | Numbers API | ✅ |
-| Verification | Verification API | ✅ |
+| API Category | API Name | Status |
+|------------------------|------------------|:------:|
+| Messaging | SMS API | ✅ |
+| | Conversation API | ✅ |
+| | Fax API | ✅ |
+| Voice and Video | Voice API | ✅ |
+| Numbers & Connectivity | Numbers API | ✅ |
+| Verification | Verification API | ✅ |
diff --git a/packages/sms/src/rest/v1/sms-service.ts b/packages/sms/src/rest/v1/sms-service.ts
index 1bffc1f8..83dd032f 100644
--- a/packages/sms/src/rest/v1/sms-service.ts
+++ b/packages/sms/src/rest/v1/sms-service.ts
@@ -1,17 +1,36 @@
import {
- SinchClientParameters,
+ SinchClientParameters, SmsRegion,
} from '@sinch/sdk-client';
import { GroupsApi } from './groups';
import { DeliveryReportsApi } from './delivery-reports';
import { BatchesApi } from './batches';
import { InboundsApi } from './inbounds';
+/**
+ * The SMS Service exposes the following APIs:
+ * - groups
+ * - batches
+ * - inbounds
+ * - deliveryReports
+ */
export class SmsService {
public readonly groups: GroupsApi;
public readonly deliveryReports: DeliveryReportsApi;
public readonly batches: BatchesApi;
public readonly inbounds: InboundsApi;
+ /**
+ * Create a new SmsService instance with its configuration. This service can be instantiated with 2 different authentication mechanisms:
+ * - OAuth2: the required properties are `projectId`, `keyId` and `keySecret`
+ * - API Token: the required properties are `servicePlanId` and `apiToken`
+ *
+ * Other supported properties:
+ * - `smsRegion`
+ * - `smsHostname`
+ * - `forceOAuth2ForSmsApi`
+ * - `forceServicePlanIdUsageForSmsApi`
+ * @param {SinchClientParameters} params - an Object containing the necessary properties to initialize the service
+ */
constructor(params: SinchClientParameters) {
this.groups = new GroupsApi(params);
this.deliveryReports = new DeliveryReportsApi(params);
@@ -21,7 +40,6 @@ export class SmsService {
/**
* Update the default hostname for each API
- *
* @param {string} hostname - The new hostname to use for all the APIs.
*/
public setHostname(hostname: string) {
@@ -30,4 +48,15 @@ export class SmsService {
this.batches.setHostname(hostname);
this.inbounds.setHostname(hostname);
}
+
+ /**
+ * Update the current region for each API
+ * @param {SmsRegion} region - The new region to use in the production URL
+ */
+ public setRegion(region: SmsRegion) {
+ this.groups.setRegion(region);
+ this.deliveryReports.setRegion(region);
+ this.batches.setRegion(region);
+ this.inbounds.setRegion(region);
+ }
}
diff --git a/packages/sms/tests/rest/v1/sms-service.test.ts b/packages/sms/tests/rest/v1/sms-service.test.ts
index 20940932..590964d9 100644
--- a/packages/sms/tests/rest/v1/sms-service.test.ts
+++ b/packages/sms/tests/rest/v1/sms-service.test.ts
@@ -1,8 +1,9 @@
-import { SinchClientParameters } from '@sinch/sdk-client';
+import { SinchClientParameters, SmsRegion } from '@sinch/sdk-client';
import { BatchesApi, DeliveryReportsApi, GroupsApi, InboundsApi, SmsService } from '../../../src';
describe('SMS Service', () => {
const DEFAULT_HOSTNAME = 'https://zt.us.sms.api.sinch.com';
+ const EUROPE_HOSTNAME = 'https://zt.eu.sms.api.sinch.com';
const CUSTOM_HOSTNAME = 'https://new.host.name';
it('should initialize all the APIs', () => {
@@ -34,9 +35,9 @@ describe('SMS Service', () => {
keyId: 'KEY_ID',
keySecret: 'KEY_SECRET',
};
+ const smsService = new SmsService(params);
// When
- const smsService = new SmsService(params);
smsService.setHostname(CUSTOM_HOSTNAME);
// Then
@@ -45,4 +46,23 @@ describe('SMS Service', () => {
expect(smsService.inbounds.getSinchClient().apiClientOptions.hostname).toBe(CUSTOM_HOSTNAME);
expect(smsService.groups.getSinchClient().apiClientOptions.hostname).toBe(CUSTOM_HOSTNAME);
});
+
+ it('should update the default region for all APIs', () => {
+ // Given
+ const params: SinchClientParameters = {
+ projectId: 'PROJECT_ID',
+ keyId: 'KEY_ID',
+ keySecret: 'KEY_SECRET',
+ };
+ const smsService = new SmsService(params);
+
+ // When
+ smsService.setRegion(SmsRegion.EUROPE);
+
+ // Then
+ expect(smsService.batches.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(smsService.deliveryReports.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(smsService.inbounds.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(smsService.groups.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ });
});
diff --git a/packages/verification/src/rest/v1/verification-service.ts b/packages/verification/src/rest/v1/verification-service.ts
index 221bb5d7..ba1f98d3 100644
--- a/packages/verification/src/rest/v1/verification-service.ts
+++ b/packages/verification/src/rest/v1/verification-service.ts
@@ -2,10 +2,24 @@ import { SinchClientParameters } from '@sinch/sdk-client';
import { VerificationStatusApi } from './verification-status';
import { VerificationsApi } from './verifications';
+/**
+ * The Verification Service exposes the following APIs:
+ * - verifications
+ * - verificationStatus
+ */
export class VerificationService {
public readonly verificationStatus: VerificationStatusApi;
public readonly verifications: VerificationsApi;
+ /**
+ * Create a new VerificationService instance with its configuration. It needs the following parameters for authentication:
+ * - `applicationKey`
+ * - `applicationSecret`
+ *
+ * Other supported properties:
+ * - `verificationHostname`
+ * @param {SinchClientParameters} params - an Object containing the necessary properties to initialize the service
+ */
constructor(params: SinchClientParameters) {
this.verificationStatus = new VerificationStatusApi(params);
this.verifications = new VerificationsApi(params);
@@ -13,7 +27,6 @@ export class VerificationService {
/**
* Update the default hostname for each API
- *
* @param {string} hostname - The new hostname to use for all the APIs.
*/
public setHostname(hostname: string) {
diff --git a/packages/voice/src/rest/v1/voice-service.ts b/packages/voice/src/rest/v1/voice-service.ts
index 28eef344..a0f8bce7 100644
--- a/packages/voice/src/rest/v1/voice-service.ts
+++ b/packages/voice/src/rest/v1/voice-service.ts
@@ -4,12 +4,30 @@ import { ConferencesApi } from './conferences';
import { CallsApi } from './calls';
import { CalloutsApi } from './callouts';
+/**
+ * The Voice Service exposes the following APIs:
+ * - applications
+ * - callouts
+ * - conferences
+ * - calls
+ */
export class VoiceService {
public readonly applications: ApplicationsApi;
public readonly conferences: ConferencesApi;
public readonly calls: CallsApi;
public readonly callouts: CalloutsApi;
+ /**
+ * Create a new VoiceService instance with its configuration. It needs the following parameters for authentication:
+ * - `applicationKey`
+ * - `applicationSecret`
+ *
+ * Other supported properties:
+ * - `voiceRegion`
+ * - `voiceHostname`
+ * - `voiceApplicationManagementHostname`
+ * @param {SinchClientParameters} params - an Object containing the necessary properties to initialize the service
+ */
constructor(params: SinchClientParameters) {
this.applications = new ApplicationsApi(params);
this.conferences = new ConferencesApi(params);
@@ -35,7 +53,13 @@ export class VoiceService {
this.applications.setHostname(hostname);
}
+ /**
+ * Update the current region for each API
+ * @param {VoiceRegion} region - The new region to use in the production URL
+ */
public setRegion(region: VoiceRegion) {
- this.applications.setRegion(region);
+ this.conferences.setRegion(region);
+ this.calls.setRegion(region);
+ this.callouts.setRegion(region);
}
}
diff --git a/packages/voice/tests/rest/v1/voice-service.test.ts b/packages/voice/tests/rest/v1/voice-service.test.ts
index 786b9472..5ec3af0b 100644
--- a/packages/voice/tests/rest/v1/voice-service.test.ts
+++ b/packages/voice/tests/rest/v1/voice-service.test.ts
@@ -1,8 +1,9 @@
-import { SinchClientParameters } from '@sinch/sdk-client';
+import { SinchClientParameters, VoiceRegion } from '@sinch/sdk-client';
import { ApplicationsApi, CalloutsApi, CallsApi, ConferencesApi, VoiceService } from '../../../src';
describe('Voice Service', () => {
const DEFAULT_HOSTNAME = 'https://calling.api.sinch.com';
+ const EUROPE_HOSTNAME = 'https://calling-euc1.api.sinch.com';
const CUSTOM_HOSTNAME = 'https://new.host.name';
const DEFAULT_HOSTNAME_APPLICATIONS = 'https://callingapi.sinch.com';
const CUSTOM_HOSTNAME_APPLICATIONS = 'https://apps.new.host.name';
@@ -48,9 +49,9 @@ describe('Voice Service', () => {
applicationKey: 'APPLICATION_KEY',
applicationSecret: 'APPLICATION_SECRET',
};
+ const voiceService = new VoiceService(params);
// When
- const voiceService = new VoiceService(params);
voiceService.setApplicationsManagementHostname(CUSTOM_HOSTNAME_APPLICATIONS);
// Then
@@ -59,4 +60,22 @@ describe('Voice Service', () => {
expect(voiceService.conferences.getSinchClient().apiClientOptions.hostname).toBe(DEFAULT_HOSTNAME);
expect(voiceService.applications.getSinchClient().apiClientOptions.hostname).toBe(CUSTOM_HOSTNAME_APPLICATIONS);
});
+
+ it('should update the default region for all APIs but for application management', () => {
+ // Given
+ const params: SinchClientParameters = {
+ applicationKey: 'APPLICATION_KEY',
+ applicationSecret: 'APPLICATION_SECRET',
+ };
+ const voiceService = new VoiceService(params);
+
+ // When
+ voiceService.setRegion(VoiceRegion.EUROPE);
+
+ // Then
+ expect(voiceService.callouts.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(voiceService.calls.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(voiceService.conferences.getSinchClient().apiClientOptions.hostname).toBe(EUROPE_HOSTNAME);
+ expect(voiceService.applications.getSinchClient().apiClientOptions.hostname).toBe(DEFAULT_HOSTNAME_APPLICATIONS);
+ });
});