Skip to content

Commit

Permalink
Merge branch 'v1.2-next' into DEVEXP-526_E2E-Voice/Conferences
Browse files Browse the repository at this point in the history
  • Loading branch information
asein-sinch authored Sep 23, 2024
2 parents 9a3b74e + dae6764 commit a73dc73
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 42 deletions.
9 changes: 0 additions & 9 deletions .github/workflows/run-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ jobs:
token: ${{ secrets.PAT_CI }}
fetch-depth: 0
path: sinch-sdk-mockserver
- name: Build custom Docker image
run: |
cd sinch-sdk-mockserver
docker build -t sinch-sdk-mockserver -f Dockerfile .
- name: Install Docker Compose
run: |
sudo apt-get update
Expand All @@ -44,11 +40,6 @@ jobs:
run: |
cd sinch-sdk-mockserver
docker-compose up -d
- name: Wait for the mock servers to be healthy
run: |
cd sinch-sdk-mockserver
chmod +x ./scripts/healthcheck.sh
./scripts/healthcheck.sh
- name: Create target directories for feature files
run: |
mkdir -p ./packages/fax/tests/e2e/features
Expand Down
7 changes: 2 additions & 5 deletions examples/webhooks/src/services/verification-event.service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Injectable } from '@nestjs/common';
import { Response } from 'express';
import {
Verification,
VerificationCallback,
} from '@sinch/sdk-core';
import { Verification } from '@sinch/sdk-core';

@Injectable()
export class VerificationEventService {

handleEvent(event: VerificationCallback, res: Response) {
handleEvent(event: Verification.VerificationCallbackEvent, res: Response) {
console.log(`:: INCOMING EVENT :: ${event.event}`);
switch (event.event) {
case 'VerificationRequestEvent':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Given('the Conversation service "Contacts" is available', function () {
contactsApi = conversationService.contact;
});

When('I send a request to create an contact', async () => {
When('I send a request to create a contact', async () => {
contact = await contactsApi.create({
contactCreateRequestBody: {
channel_identities: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ const processEvent = async (response: Response) => {
formattedHeaders[name.toLowerCase()] = value;
});
rawEvent = await response.text();
rawEvent = rawEvent.replace(/\s+/g, '');
event = conversationCallbackWebhook.parseEvent(JSON.parse(rawEvent));
};

Given('the Conversation Webhooks handler is available', () => {
conversationCallbackWebhook = new ConversationCallbackWebhooks(APP_SECRET);
});

Then('the Conversation event header contains a valid signature', () => {
assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent));
});

When('I send a request to trigger a "CAPABILITY" event', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/capability-lookup');
await processEvent(response);
});

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Then('the header of the Conversation event {string} contains a valid signature', (_event) => {
assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent));
});

Then('the Conversation event describes a "CAPABILITY" event type', () => {
const capabilityEvent = event as Conversation.CapabilityEvent;
assert.ok(capabilityEvent.capability_notification);
Expand Down Expand Up @@ -123,11 +123,16 @@ Then('the Conversation event describes a "CONVERSATION_STOP" event type', () =>
assert.equal(conversationStopEvent.trigger, expectedTrigger);
});

When('I send a request to trigger a "EVENT_DELIVERY" event with a FAILED status', async () => {
When('I send a request to trigger a "EVENT_DELIVERY" event with a "FAILED" status', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/event-delivery-report/failed');
await processEvent(response);
});

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Then('the header of the Conversation event {} with a {} status contains a valid signature', (_event, _status) => {
assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent));
});

Then('the Conversation event describes a "EVENT_DELIVERY" event type', () => {
const eventDeliveryEvent = event as Conversation.EventDelivery;
assert.ok(eventDeliveryEvent.event_delivery_report);
Expand All @@ -144,7 +149,7 @@ Then('the Conversation event describes a FAILED event delivery status and its re
assert.equal(eventDeliveryReport.reason.code, expectedReasonCode);
});

When('I send a request to trigger a "EVENT_DELIVERY" event with a DELIVERED status', async () => {
When('I send a request to trigger a "EVENT_DELIVERY" event with a "DELIVERED" status', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/event-delivery-report/succeeded');
await processEvent(response);
});
Expand All @@ -161,12 +166,12 @@ Then('the Conversation event describes a "EVENT_INBOUND" event type', () => {
assert.equal(eventInbound.trigger, expectedTrigger);
});

When('I send a request to trigger a "MESSAGE_DELIVERY" event with a FAILED status', async () => {
When('I send a request to trigger a "MESSAGE_DELIVERY" event with a "FAILED" status', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/message-delivery-report/failed');
await processEvent(response);
});

When('I send a request to trigger a "MESSAGE_DELIVERY" event with a QUEUED status', async () => {
When('I send a request to trigger a "MESSAGE_DELIVERY" event with a "QUEUED_ON_CHANNEL" status', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/message-delivery-report/succeeded');
await processEvent(response);
});
Expand Down Expand Up @@ -212,38 +217,43 @@ Then('the Conversation event describes a "MESSAGE_INBOUND_SMART_CONVERSATION_RED
assert.equal(messageInboundSmartConversationRedactionEvent.trigger, expectedTrigger);
});

When('I send a request to trigger a "MESSAGE_SUBMIT" event for a media message', async () => {
When('I send a request to trigger a "MESSAGE_SUBMIT" event for a "media" message', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/message-submit/media');
await processEvent(response);
});

Then('the Conversation event describes a "MESSAGE_SUBMIT" event type for a media message', () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Then('the header of the Conversation event {} for a {} message contains a valid signature', (_event, _messageType) => {
assert.ok(conversationCallbackWebhook.validateAuthenticationHeader(formattedHeaders, rawEvent));
});

Then('the Conversation event describes a "MESSAGE_SUBMIT" event type for a "media" message', () => {
const messageSubmitEvent = event as Conversation.MessageSubmitEvent;
assert.ok(messageSubmitEvent.message_submit_notification);
const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_SUBMIT';
assert.equal(messageSubmitEvent.trigger, expectedTrigger);
assert.ok(messageSubmitEvent.message_submit_notification.submitted_message?.media_message);
});

When('I send a request to trigger a "MESSAGE_SUBMIT" event for a text message', async () => {
When('I send a request to trigger a "MESSAGE_SUBMIT" event for a "text" message', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/message-submit/text');
await processEvent(response);
});

Then('the Conversation event describes a "MESSAGE_SUBMIT" event type for a text message', () => {
Then('the Conversation event describes a "MESSAGE_SUBMIT" event type for a "text" message', () => {
const messageSubmitEvent = event as Conversation.MessageSubmitEvent;
assert.ok(messageSubmitEvent.message_submit_notification);
const expectedTrigger: Conversation.WebhookTrigger = 'MESSAGE_SUBMIT';
assert.equal(messageSubmitEvent.trigger, expectedTrigger);
assert.ok(messageSubmitEvent.message_submit_notification.submitted_message?.text_message);
});

When('I send a request to trigger a "SMART_CONVERSATIONS" event for a media message', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/smart-conversation/media');
When('I send a request to trigger a "SMART_CONVERSATIONS" event for a "media" message', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/smart-conversations/media');
await processEvent(response);
});

Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a media message', () => {
Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a "media" message', () => {
const smartConversationsEvent = event as Conversation.SmartConversationsEvent;
assert.ok(smartConversationsEvent.smart_conversation_notification);
const expectedTrigger: Conversation.WebhookTrigger = 'SMART_CONVERSATIONS';
Expand All @@ -252,12 +262,12 @@ Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a
assert.ok(smartConversationsEvent.smart_conversation_notification.analysis_results?.ml_offensive_analysis_result);
});

When('I send a request to trigger a "SMART_CONVERSATIONS" event for a text message', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/smart-conversation/text');
When('I send a request to trigger a "SMART_CONVERSATIONS" event for a "text" message', async () => {
const response = await fetch('http://localhost:3014/webhooks/conversation/smart-conversations/text');
await processEvent(response);
});

Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a text message', () => {
Then('the Conversation event describes a "SMART_CONVERSATIONS" event type for a "text" message', () => {
const smartConversationsEvent = event as Conversation.SmartConversationsEvent;
assert.ok(smartConversationsEvent.smart_conversation_notification);
const expectedTrigger: Conversation.WebhookTrigger = 'SMART_CONVERSATIONS';
Expand Down
2 changes: 0 additions & 2 deletions packages/verification/src/models/v1/identity/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@ export interface Identity {
type: 'number';
/** For type `number` use an [E.164](https://community.sinch.com/t5/Glossary/E-164/ta-p/7537)-compatible phone number. */
endpoint: string;
/** */
verified?: boolean;
}
1 change: 1 addition & 0 deletions packages/verification/src/models/v1/mod-callbacks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './verification-callback-event';
// 'Verification Request Event' received from Sinch server
export * from './verification-request-event';
// Response to send to Sinch server for a 'Verification Request Event'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { VerificationRequestEvent } from './verification-request-event';
import { VerificationResultEvent } from './verification-result-event';

export type VerificationCallbackEvent = VerificationRequestEvent | VerificationResultEvent;
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export type { VerificationRequestEvent } from './verification-request-event';
export type { VerificationRequestEvent, MethodEnum } from './verification-request-event';
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { VerificationRequestEvent, VerificationResultEvent } from '../../../models';
import { VerificationCallbackEvent, VerificationRequestEvent, VerificationResultEvent } from '../../../models';
import { CallbackProcessor, SinchClientParameters, validateAuthenticationHeader } from '@sinch/sdk-client';
import { IncomingHttpHeaders } from 'http';

/** @deprecated - use Verification.VerificationCallback instead */
export type VerificationCallback = VerificationRequestEvent | VerificationResultEvent;

export class VerificationCallbackWebhooks implements CallbackProcessor<VerificationCallback>{
export class VerificationCallbackWebhooks implements CallbackProcessor<VerificationCallbackEvent>{

private readonly sinchClientParameters: SinchClientParameters;

Expand Down Expand Up @@ -39,9 +40,9 @@ export class VerificationCallbackWebhooks implements CallbackProcessor<Verificat
* Reviver for a Verification Event.
* This method ensures the object can be treated as a Verification Event and should be called before any action is taken to manipulate the object.
* @param {any} eventBody - The event body containing the verification event notification.
* @return {VerificationCallback} - The parsed verification event object.
* @return {VerificationCallbackEvent} - The parsed verification event object.
*/
public parseEvent(eventBody: any): VerificationCallback {
public parseEvent(eventBody: any): VerificationCallbackEvent {
if (eventBody.event) {
switch (eventBody.event) {
case 'VerificationRequestEvent':
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { VerificationCallbackWebhooks, Verification } from '../../../../src';
import { Given, Then, When } from '@cucumber/cucumber';
import assert from 'assert';
import { IncomingHttpHeaders } from 'http';

let verificationCallbackWebhook: VerificationCallbackWebhooks;
let rawEvent: any;
let event: Verification.VerificationCallbackEvent;
let formattedHeaders: IncomingHttpHeaders;

const processEvent = async (response: Response) => {
formattedHeaders = {};
response.headers.forEach((value, name) => {
formattedHeaders[name.toLowerCase()] = value;
});
rawEvent = await response.text();
event = verificationCallbackWebhook.parseEvent(JSON.parse(rawEvent));
};

Given('the Verification Webhooks handler is available', () => {
verificationCallbackWebhook = new VerificationCallbackWebhooks({
applicationKey: 'appKey',
applicationSecret: 'appSecret',
});
});

When('I send a request to trigger a "Verification Request" event', async () => {
const response = await fetch('http://localhost:3018/webhooks/verification/verification-request-event');
await processEvent(response);
});

Then('the header of the Verification event "Verification Request" contains a valid authorization', () => {
assert.ok(verificationCallbackWebhook.validateAuthenticationHeader(
formattedHeaders,
rawEvent,
'/webhooks/verification',
'POST'));
});

Then('the Verification event describes a "Verification Request" event type', () => {
const verificationRequestEvent = event as Verification.VerificationRequestEvent;
assert.equal(verificationRequestEvent.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337');
assert.equal(verificationRequestEvent.event, 'VerificationRequestEvent');
const smsVerificationMethod: Verification.MethodEnum = 'sms';
assert.equal(verificationRequestEvent.method, smsVerificationMethod);
const identity: Verification.Identity = {
type: 'number',
endpoint: '+33612345678',
};
assert.equal(verificationRequestEvent.identity.type, identity.type);
assert.equal(verificationRequestEvent.identity.endpoint, identity.endpoint);
const smsPrice: Verification.Price = {
currencyId: 'EUR',
amount: 0.0453,
};
assert.deepEqual(verificationRequestEvent.price, smsPrice);
const smsRate: Verification.Price = {
currencyId: 'EUR',
amount: 0,
};
assert.deepEqual(verificationRequestEvent.rate, smsRate);
});

When('I send a request to trigger a "Verification Result" event', async () => {
const response = await fetch('http://localhost:3018/webhooks/verification/verification-result-event');
await processEvent(response);
});

Then('the header of the Verification event "Verification Result" contains a valid authorization', () => {
assert.ok(verificationCallbackWebhook.validateAuthenticationHeader(
formattedHeaders,
rawEvent,
'/webhooks/verification',
'POST'));
});

Then('the Verification event describes a "Verification Result" event type', () => {
const verificationRequestEvent = event as Verification.VerificationResultEvent;
assert.equal(verificationRequestEvent.id, '1ce0ffee-c0de-5eed-d00d-f00dfeed1337');
assert.equal(verificationRequestEvent.event, 'VerificationResultEvent');
const smsVerificationMethod: Verification.MethodEnum = 'sms';
assert.equal(verificationRequestEvent.method, smsVerificationMethod);
const identity: Verification.Identity = {
type: 'number',
endpoint: '+33612345678',
};
assert.equal(verificationRequestEvent.identity.type, identity.type);
assert.equal(verificationRequestEvent.identity.endpoint, identity.endpoint);
});
2 changes: 1 addition & 1 deletion packages/voice/tests/rest/v1/calls/calls.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ When('I send a request to update a call that doesn\'t exist', async () => {
}
});

Then('the update call response contains an error', () => {
Then('the update call response contains a "not found" error', () => {
assert.equal(updateCallResponse, undefined);
const voiceError = JSON.parse(error.data) as Voice.VoiceError;
assert.equal(voiceError.errorCode, 40400);
Expand Down

0 comments on commit a73dc73

Please sign in to comment.