From 4d9540ea958df8ef6f918bc51222b80d02106163 Mon Sep 17 00:00:00 2001 From: Sam Taylor <15961687+itssamtaylor@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:36:27 -0400 Subject: [PATCH] Fixes NCCO parsing to and from arrays (#495) * update stream factory to accept arrays and strings * Add tests, fix NCCO action factories casting incorrect data types --------- Co-authored-by: Sam Taylor <15961687+samueljtaylor@users.noreply.github.com> --- src/Voice/NCCO/Action/Input.php | 6 + src/Voice/NCCO/Action/Notify.php | 3 + src/Voice/NCCO/Action/Record.php | 11 +- src/Voice/NCCO/Action/Stream.php | 7 +- test/Voice/NCCO/Action/StreamTest.php | 16 ++ test/Voice/NCCO/NCCOTest.php | 267 ++++++++++++++++++++++++++ 6 files changed, 306 insertions(+), 4 deletions(-) diff --git a/src/Voice/NCCO/Action/Input.php b/src/Voice/NCCO/Action/Input.php index 2bbafa0e..4eacbaa9 100644 --- a/src/Voice/NCCO/Action/Input.php +++ b/src/Voice/NCCO/Action/Input.php @@ -84,6 +84,9 @@ public static function factory(array $data): Input if (array_key_exists('dtmf', $data)) { $dtmf = $data['dtmf']; $action->setEnableDtmf(true); + if (is_object($dtmf)) { + $dtmf = (array)$dtmf; + } if (array_key_exists('timeOut', $dtmf)) { $action->setDtmfTimeout((int)$dtmf['timeOut']); @@ -103,6 +106,9 @@ public static function factory(array $data): Input if (array_key_exists('speech', $data)) { $speech = $data['speech']; $action->setEnableSpeech(true); + if (is_object($speech)) { + $speech = (array)$speech; + } if (array_key_exists('uuid', $speech)) { $action->setSpeechUUID($speech['uuid'][0]); diff --git a/src/Voice/NCCO/Action/Notify.php b/src/Voice/NCCO/Action/Notify.php index e9e3719b..48191502 100644 --- a/src/Voice/NCCO/Action/Notify.php +++ b/src/Voice/NCCO/Action/Notify.php @@ -21,6 +21,9 @@ public function __construct(protected array $payload, protected ?\Vonage\Voice\W public static function factory(array $payload, array $data): Notify { if (array_key_exists('eventUrl', $data)) { + if (is_array($data['eventUrl'])) { + $data['eventUrl'] = $data['eventUrl'][0]; + } if (array_key_exists('eventMethod', $data)) { $webhook = new Webhook($data['eventUrl'], $data['eventMethod']); } else { diff --git a/src/Voice/NCCO/Action/Record.php b/src/Voice/NCCO/Action/Record.php index 23cf690c..927bdb2c 100644 --- a/src/Voice/NCCO/Action/Record.php +++ b/src/Voice/NCCO/Action/Record.php @@ -71,7 +71,9 @@ public static function factory(array $data): self } if (array_key_exists('channels', $data)) { - $action->setChannels($data['channels']); + $action->setChannels( + filter_var($data['channels'], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE) + ); } if (array_key_exists('endOnSilence', $data)) { @@ -85,7 +87,9 @@ public static function factory(array $data): self } if (array_key_exists('timeOut', $data)) { - $action->setTimeout($data['timeOut']); + $action->setTimeout( + filter_var($data['timeOut'], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE) + ); } if (array_key_exists('beepStart', $data)) { @@ -95,6 +99,9 @@ public static function factory(array $data): self } if (array_key_exists('eventUrl', $data)) { + if (is_array($data['eventUrl'])) { + $data['eventUrl'] = $data['eventUrl'][0]; + } if (array_key_exists('eventMethod', $data)) { $webhook = new Webhook($data['eventUrl'], $data['eventMethod']); } else { diff --git a/src/Voice/NCCO/Action/Stream.php b/src/Voice/NCCO/Action/Stream.php index 7171dec4..90536760 100644 --- a/src/Voice/NCCO/Action/Stream.php +++ b/src/Voice/NCCO/Action/Stream.php @@ -30,10 +30,13 @@ public function __construct(protected string $streamUrl) } /** - * @param array{streamUrl: string, bargeIn?: bool, level?: float, loop?: int, voiceName?: string} $data + * @param array{streamUrl: string|array, bargeIn?: bool, level?: float, loop?: int, voiceName?: string} $data */ - public static function factory(string $streamUrl, array $data): Stream + public static function factory(string|array $streamUrl, array $data): Stream { + if (is_array($streamUrl)) { + $streamUrl = $streamUrl[0]; + } $stream = new Stream($streamUrl); if (array_key_exists('bargeIn', $data)) { diff --git a/test/Voice/NCCO/Action/StreamTest.php b/test/Voice/NCCO/Action/StreamTest.php index 1fc04c66..48128e48 100644 --- a/test/Voice/NCCO/Action/StreamTest.php +++ b/test/Voice/NCCO/Action/StreamTest.php @@ -31,4 +31,20 @@ public function testJsonSerializeLooksCorrect(): void ->setLoop(1) ->jsonSerialize()); } + + public function testFactoryWithArray(): void + { + $this->assertSame([ + 'action' => 'stream', + 'streamUrl' => ['https://test.domain/music.mp3'] + ], Stream::factory(['https://test.domain/music.mp3'], [])->toNCCOArray()); + } + + public function testFactoryWithString(): void + { + $this->assertSame([ + 'action' => 'stream', + 'streamUrl' => ['https://test.domain/music.mp3'] + ], Stream::factory('https://test.domain/music.mp3', [])->toNCCOArray()); + } } diff --git a/test/Voice/NCCO/NCCOTest.php b/test/Voice/NCCO/NCCOTest.php index af69ac59..11874657 100644 --- a/test/Voice/NCCO/NCCOTest.php +++ b/test/Voice/NCCO/NCCOTest.php @@ -89,4 +89,271 @@ public function testCanCreateNCCOFromArray(): void $this->assertCount(7, $json); $this->assertEquals($data[0], $json[0]); } + + public function testCanCreateFromValidNCCOArray(): void + { + $data = [ + [ + "action" => "talk", + "text" => "Thank you for trying Vonage", + "bargeIn" => "false", + "level" => "0", + "loop" => "1", + "language" => "en-US", + "style" => "0", + "premium" => "false", + ], + [ + "action" => "record", + "format" => "wav", + "beepStart" => "true", + "endOnSilence" => "4", + "endOnKey" => "#", + "channels" => "12", + "split" => "conversation", + "timeOut" => "7200", + "eventUrl" => [ + "http://domain.test/event", + ], + "eventMethod" => "POST", + ], + [ + "action" => "conversation", + "name" => "Sample Conversation", + "startOnEnter" => "true", + "endOnExit" => "false", + "record" => "true", + "canSpeak" => [ + "49502bca-da71-44bb-b3a6-5077b58c2690", + ], + "canHear" => [ + "798146f0-af79-468a-83a4-b6fcda7cd4e6", + ], + ], + [ + "action" => "connect", + "endpoint" => [ + [ + "type" => "phone", + "number" => "447700900001", + ], + ], + ], + [ + "action" => "talk", + "text" => "Thank you for trying Vonage", + "bargeIn" => "false", + "level" => "0", + "loop" => "1", + "language" => "en-US", + "style" => "0", + "premium" => "false", + ], + [ + "action" => "record", + "format" => "wav", + "beepStart" => "true", + "endOnSilence" => "4", + "endOnKey" => "#", + "channels" => "12", + "split" => "conversation", + "timeOut" => "7200", + "eventUrl" => [ + "http://domain.test/event", + ], + "eventMethod" => "POST", + ], + [ + "action" => "conversation", + "name" => "Sample Conversation", + "startOnEnter" => "true", + "endOnExit" => "false", + "record" => "true", + "canSpeak" => [ + "49502bca-da71-44bb-b3a6-5077b58c2690", + ], + "canHear" => [ + "798146f0-af79-468a-83a4-b6fcda7cd4e6", + ], + ], + [ + "action" => "connect", + "endpoint" => [ + [ + "type" => "phone", + "number" => "447700900001", + ], + ], + ], + [ + "action" => "stream", + "streamUrl" => [ + "http://domain.test/music.mp3", + ], + "bargeIn" => "true", + "level" => "0.1", + "loop" => "0", + ], + [ + "action" => "input", + "dtmf" => [ + "maxDigits" => 1, + ], + "speech" => [ + "uuid" => [ + "49502bca-da71-44bb-b3a6-5077b58c2690", + ], + "maxDuration" => 30, + ], + ], + [ + "action" => "notify", + "payload" => [ + "foo" => "bar", + ], + "eventUrl" => [ + "http://domain.test/event", + ], + "eventMethod" => "POST", + ], + ]; + $ncco = new NCCO(); + $ncco->fromArray($data); + $this->assertEquals(json_encode($data), json_encode($ncco)); + } + + public function testCanConvertToAndFromArray(): void + { + $data = [ + [ + "action" => "talk", + "text" => "Thank you for trying Vonage", + "bargeIn" => "false", + "level" => "0", + "loop" => "1", + "language" => "en-US", + "style" => "0", + "premium" => "false", + ], + [ + "action" => "record", + "format" => "wav", + "beepStart" => "true", + "endOnSilence" => "4", + "endOnKey" => "#", + "channels" => "12", + "split" => "conversation", + "timeOut" => "7200", + "eventUrl" => [ + "http://domain.test/event", + ], + "eventMethod" => "POST", + ], + [ + "action" => "conversation", + "name" => "Sample Conversation", + "startOnEnter" => "true", + "endOnExit" => "false", + "record" => "true", + "canSpeak" => [ + "49502bca-da71-44bb-b3a6-5077b58c2690", + ], + "canHear" => [ + "798146f0-af79-468a-83a4-b6fcda7cd4e6", + ], + ], + [ + "action" => "connect", + "endpoint" => [ + [ + "type" => "phone", + "number" => "447700900001", + ], + ], + ], + [ + "action" => "talk", + "text" => "Thank you for trying Vonage", + "bargeIn" => "false", + "level" => "0", + "loop" => "1", + "language" => "en-US", + "style" => "0", + "premium" => "false", + ], + [ + "action" => "record", + "format" => "wav", + "beepStart" => "true", + "endOnSilence" => "4", + "endOnKey" => "#", + "channels" => "12", + "split" => "conversation", + "timeOut" => "7200", + "eventUrl" => [ + "http://domain.test/event", + ], + "eventMethod" => "POST", + ], + [ + "action" => "conversation", + "name" => "Sample Conversation", + "startOnEnter" => "true", + "endOnExit" => "false", + "record" => "true", + "canSpeak" => [ + "49502bca-da71-44bb-b3a6-5077b58c2690", + ], + "canHear" => [ + "798146f0-af79-468a-83a4-b6fcda7cd4e6", + ], + ], + [ + "action" => "connect", + "endpoint" => [ + [ + "type" => "phone", + "number" => "447700900001", + ], + ], + ], + [ + "action" => "stream", + "streamUrl" => [ + "http://domain.test/music.mp3", + ], + "bargeIn" => "true", + "level" => "0.1", + "loop" => "0", + ], + [ + "action" => "input", + "dtmf" => [ + "maxDigits" => 1, + ], + "speech" => [ + "uuid" => [ + "49502bca-da71-44bb-b3a6-5077b58c2690", + ], + "maxDuration" => 30, + ], + ], + [ + "action" => "notify", + "payload" => [ + "foo" => "bar", + ], + "eventUrl" => [ + "http://domain.test/event", + ], + "eventMethod" => "POST", + ], + ]; + $ncco1 = new NCCO(); + $ncco2 = new NCCO(); + $ncco1->fromArray($data); + $ncco2->fromArray($ncco1->toArray()); + $this->assertEquals($ncco1->toArray(), $ncco2->toArray()); + $this->assertEquals(json_encode($data), json_encode($ncco2)); + } }