From cb6b39aa0bb58bbe8f0f683cf919e02edf3ee810 Mon Sep 17 00:00:00 2001 From: Manik Sachdeva Date: Mon, 13 Aug 2018 10:31:58 -0700 Subject: [PATCH] release (#222) * release 4.2.0 and update branch --- README.md | 82 +++++ ...ForceDisconnectAuthenticationException.php | 10 + .../ForceDisconnectConnectionException.php | 15 + .../Exception/ForceDisconnectException.php | 10 + ...orceDisconnectUnexpectedValueException.php | 15 + .../SignalAuthenticationException.php | 10 + .../Exception/SignalConnectionException.php | 15 + src/OpenTok/Exception/SignalException.php | 10 + .../SignalUnexpectedValueException.php | 15 + src/OpenTok/OpenTok.php | 108 ++++++- src/OpenTok/Session.php | 1 - src/OpenTok/Stream.php | 33 ++ src/OpenTok/StreamList.php | 42 +++ src/OpenTok/Util/Client.php | 121 ++++++- src/OpenTok/Util/Validators.php | 10 + tests/OpenTok/OpenTokTest.php | 304 +++++++++++++++++- tests/OpenTok/SessionTest.php | 51 +++ .../mock/v2/project/APIKEY/archive/get_third | 26 ++ .../session/SESSIONID/stream/STREAMID/get | 6 + .../APIKEY/session/SESSIONID/stream/get | 17 + 20 files changed, 892 insertions(+), 9 deletions(-) create mode 100644 src/OpenTok/Exception/ForceDisconnectAuthenticationException.php create mode 100644 src/OpenTok/Exception/ForceDisconnectConnectionException.php create mode 100644 src/OpenTok/Exception/ForceDisconnectException.php create mode 100644 src/OpenTok/Exception/ForceDisconnectUnexpectedValueException.php create mode 100644 src/OpenTok/Exception/SignalAuthenticationException.php create mode 100644 src/OpenTok/Exception/SignalConnectionException.php create mode 100644 src/OpenTok/Exception/SignalException.php create mode 100644 src/OpenTok/Exception/SignalUnexpectedValueException.php create mode 100644 src/OpenTok/Stream.php create mode 100644 src/OpenTok/StreamList.php create mode 100644 tests/mock/v2/project/APIKEY/archive/get_third create mode 100644 tests/mock/v2/project/APIKEY/session/SESSIONID/stream/STREAMID/get create mode 100644 tests/mock/v2/project/APIKEY/session/SESSIONID/stream/get diff --git a/README.md b/README.md index e7e9b511..64ec7e62 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,36 @@ $token = $session->generateToken(array( )); ``` +## Working with Streams + +You can get information about a stream by calling the `getStream($sessionId, $streamId)` method of the +`OpenTok\OpenTok` class. + +```php +use OpenTok\Session; + +// Get stream info from just a sessionId (fetched from a database) +$stream = $opentok->getStream($sessionId, $streamId); + +// Stream properties +$stream->id; // string with the stream ID +$stream->videoType; // string with the video type +$stream->name; // string with the name +$stream->layoutClassList; // array with the layout class list +``` + +You can get information about all the streams in a session by calling the `listStreams($sessionId)` method of the +`OpenTok\OpenTok` class. + +```php +use OpenTok\Session; + +// Get list of streams from just a sessionId (fetched from a database) +$streamList = $opentok->listStreams($sessionId); + +$streamList->totalCount(); // total count +``` + ## Working with Archives You can only archive sessions that use the OpenTok Media Router @@ -193,6 +223,58 @@ method (see "Creating Sessions," above). For more information on archiving, see the [OpenTok archiving](https://tokbox.com/opentok/tutorials/archiving/) programming guide. +## Force Disconnect + +Your application server can disconnect a client from an OpenTok session by calling the `forceDisconnect($sessionId, $connectionId)` +method of the `OpenTok\OpenTok` class. + +```php +use OpenTok\OpenTok; + +// Force disconnect a client connection +$opentok->forceDisconnect($sessionId, $connectionId); +``` +## Sending Signals + +Once a Session is created, you can send signals to everyone in the session or to a specific connection. +You can send a signal by calling the `signal($sessionId, $payload, $connectionId)` method of the +`OpenTok\OpenTok` class. + +The `$sessionId` parameter is the session ID of the session. + +The `$payload` parameter is an associative array used to set the +following: + +* `data` (string) -- The data string for the signal. You can send a maximum of 8kB. + +* `type` (string) -- — (Optional) The type string for the signal. You can send a maximum of 128 characters, and only the following characters are allowed: A-Z, a-z, numbers (0-9), '-', '_', and '~'. + +The `$connectionId` parameter is an optional string used to specify the connection ID of +a client connected to the session. If you specify this value, the signal is sent to +the specified client. Otherwise, the signal is sent to all clients connected to the session. + + +```php +use OpenTok\OpenTok; + +// Send a signal to a specific client +$signalPayload = array( + 'data' => 'some signal message', + 'type' => 'signal type' +); +$connectionId = 'da9cb410-e29b-4c2d-ab9e-fe65bf83fcaf'; +$opentok->signal($sessionId, $signalPayload, $connectionId); + +// Send a signal to everyone in the session +$signalPayload = array( + 'data' => 'some signal message', + 'type' => 'signal type' +); +$opentok->signal($sessionId, $signalPayload); +``` + +For more information, see the [OpenTok signaling developer +guide](https://tokbox.com/developer/guides/signaling/). # Samples diff --git a/src/OpenTok/Exception/ForceDisconnectAuthenticationException.php b/src/OpenTok/Exception/ForceDisconnectAuthenticationException.php new file mode 100644 index 00000000..dc8d75df --- /dev/null +++ b/src/OpenTok/Exception/ForceDisconnectAuthenticationException.php @@ -0,0 +1,10 @@ +apiKey); + } - $archiveListData = $this->client->listArchives($offset, $count); + $archiveListData = $this->client->listArchives($offset, $count, $sessionId); return new ArchiveList($archiveListData, array( 'client' => $this->client )); } + /** + * Force disconnects a specific client connected to an OpenTok session. + * + * @param string $sessionId The OpenTok session ID where the signal will be sent. + * + * @param string $connectionId The connectionId of the connection in a session. + */ + public function forceDisconnect($sessionId, $connectionId) { - Validators::validateSessionId($sessionId); + Validators::validateSessionIdBelongsToKey($sessionId, $this->apiKey); Validators::validateConnectionId($connectionId); return $this->client->forceDisconnect($sessionId, $connectionId); @@ -490,6 +506,46 @@ public function updateStream($sessionId, $streamId, $properties = array()) $this->client->updateStream($sessionId, $streamId, $properties); } + /** + * Gets an Stream object for the given stream ID. + * + * @param String $sessionId The session ID. + * + * @param String $streamId The stream ID. + * + * @return Stream The Stream object. + */ + + public function getStream($sessionId, $streamId) + { + Validators::validateSessionId($sessionId); + Validators::validateStreamId($streamId); + + // make API call + $streamData = $this->client->getStream($sessionId, $streamId); + return new Stream($streamData); + + } + + /** + * Returns a StreamList Object for the given session ID. + * + * @param String $sessionId The session ID. + * + * @return StreamList A StreamList object. Call the items() method of the StreamList object + * to return an array of Stream objects. + */ + + public function listStreams($sessionId) + { + Validators::validateSessionIdBelongsToKey($sessionId, $this->apiKey); + + // make API call + $streamListData = $this->client->listStreams($sessionId); + return new StreamList($streamListData); + + } + /** * Initiate an outgoing SIP call * @@ -566,6 +622,52 @@ public function dial($sessionId, $token, $sipUri, $options=array()) return new SipCall($sipJson); } + /** + * Sends a signal to clients (or a specific client) connected to an OpenTok session. + * + * @param string $sessionId The OpenTok session ID where the signal will be sent. + * + * + * @param array $payload This array defines the payload for the signal. This array includes the + * following keys, of which type is optional: + * + * + * + * + * @param string $connectionId An optional parameter used to send the signal to a specific connection in a session. + */ + public function signal($sessionId, $payload, $connectionId=null) + { + + // unpack optional arguments (merging with default values) into named variables + $defaults = array( + 'type' => '', + 'data' => '', + ); + + $payload = array_merge($defaults, array_intersect_key($payload, $defaults)); + list($type, $data) = array_values($payload); + + // validate arguments + Validators::validateSessionIdBelongsToKey($sessionId, $this->apiKey); + Validators::validateSignalPayload($payload); + + if (is_null($connectionId) || empty($connectionId)) { + // make API call without connectionId + $this->client->signal($sessionId, $payload); + } else { + Validators::validateConnectionId($connectionId); + // make API call with connectionId + $this->client->signal($sessionId, $payload, $connectionId); + } + + } + /** @internal */ private function _sign_string($string, $secret) { diff --git a/src/OpenTok/Session.php b/src/OpenTok/Session.php index 330bad0b..ae3042f5 100644 --- a/src/OpenTok/Session.php +++ b/src/OpenTok/Session.php @@ -144,7 +144,6 @@ public function generateToken($options = array()) { return $this->opentok->generateToken($this->sessionId, $options); } - } /* vim: set ts=4 sw=4 tw=100 sts=4 et :*/ diff --git a/src/OpenTok/Stream.php b/src/OpenTok/Stream.php new file mode 100644 index 00000000..c5fdd425 --- /dev/null +++ b/src/OpenTok/Stream.php @@ -0,0 +1,33 @@ +data = $streamData; + } + + public function __get($name) + { + switch ($name) { + case 'id': + case 'videoType': + case 'name': + case 'layoutClassList': + return $this->data[$name]; + break; + default: + return null; + } + } + + public function jsonSerialize() + { + return $this->data; + } +} diff --git a/src/OpenTok/StreamList.php b/src/OpenTok/StreamList.php new file mode 100644 index 00000000..24cfa5c5 --- /dev/null +++ b/src/OpenTok/StreamList.php @@ -0,0 +1,42 @@ +data = $streamListData; + } + + /** + * Returns the number of total streams for the session ID. + */ + public function totalCount() + { + return $this->data['count']; + } + + /** + * Returns an array of Stream objects. + */ + public function getItems() + { + if (!$this->items) { + $items = array(); + foreach($this->data['items'] as $streamData) { + $items[] = new Stream($streamData); + } + $this->items = $items; + } + return $this->items; + } + + + public function jsonSerialize() + { + return $this->data; + } +} diff --git a/src/OpenTok/Util/Client.php b/src/OpenTok/Util/Client.php index 1ce55ffd..54599529 100755 --- a/src/OpenTok/Util/Client.php +++ b/src/OpenTok/Util/Client.php @@ -24,6 +24,16 @@ use OpenTok\Exception\BroadcastDomainException; use OpenTok\Exception\BroadcastUnexpectedValueException; use OpenTok\Exception\BroadcastAuthenticationException; + +use OpenTok\Exception\SignalException; +use OpenTok\Exception\SignalConnectionException; +use OpenTok\Exception\SignalUnexpectedValueException; +use OpenTok\Exception\SignalAuthenticationException; + +use OpenTok\Exception\ForceDisconnectConnectionException; +use OpenTok\Exception\ForceDisconnectUnexpectedValueException; +use OpenTok\Exception\ForceDisconnectAuthenticationException; + use OpenTok\MediaMode; // TODO: build this dynamically @@ -231,13 +241,13 @@ public function forceDisconnect($sessionId,$connectionId) json_decode($response->getBody(), true); } } catch (\Exception $e) { - $this->handleException($e); + $this->handleForceDisconnectException($e); return false; } return true; } - public function listArchives($offset, $count) + public function listArchives($offset, $count, $sessionId) { $request = new Request('GET', '/v2/project/'.$this->apiKey.'/archive'); $queryParams = []; @@ -247,6 +257,9 @@ public function listArchives($offset, $count) if (!empty($count)) { $queryParams['count'] = $count; } + if (!empty($sessionId)) { + $queryParams['sessionId'] = $sessionId; + } try { $response = $this->client->send($request, [ 'debug' => $this->isDebug(), @@ -372,6 +385,42 @@ public function updateStream($sessionId, $streamId, $properties) } } + public function getStream($sessionId, $streamId) { + $request = new Request( + 'GET', + '/v2/project/'.$this->apiKey.'/session/'.$sessionId.'/stream/'.$streamId + ); + + try { + $response = $this->client->send($request, [ + 'debug' => $this->isDebug() + ]); + $streamJson = json_decode($response->getBody(), true); + } catch (\Exception $e) { + $this->handleException($e); + return; + } + return $streamJson; + } + + public function listStreams($sessionId) + { + $request = new Request( + 'GET', + '/v2/project/'.$this->apiKey.'/session/'.$sessionId.'/stream/' + ); + try { + $response = $this->client->send($request, [ + 'debug' => $this->isDebug(), + ]); + $streamListJson = json_decode($response->getBody(), true); + } catch (\Exception $e) { + $this->handleException($e); + return; + } + return $streamListJson; + } + public function dial($sessionId, $token, $sipUri, $options) { $body = array( @@ -409,6 +458,30 @@ public function dial($sessionId, $token, $sipUri, $options) return $sipJson; } + public function signal($sessionId, $options=array(), $connectionId=null) + { + // set up the request + + + $request = is_null($connectionId) || empty($connectionId) ? + new Request('POST', '/v2/project/'.$this->apiKey.'/session/'.$sessionId.'/signal') + : new Request('POST', '/v2/project/'.$this->apiKey.'/session/'.$sessionId.'/connection/'.$connectionId.'/signal'); + + try { + $response = $this->client->send($request, [ + 'debug' => $this->isDebug(), + 'json' => array_merge( + $options + ) + ]); + if ($response->getStatusCode() != 204) { + json_decode($response->getBody(), true); + } + } catch (\Exception $e) { + $this->handleSignalingException($e); + } + } + // Helpers private function postFieldsForOptions($options) @@ -492,6 +565,50 @@ private function handleBroadcastException($e) } } + private function handleSignalingException($e) + { + $responseCode = $e->getResponse()->getStatusCode(); + switch($responseCode) { + case 400: + $message = 'One of the signal properties — data, type, sessionId or connectionId — is invalid.'; + throw new SignalUnexpectedValueException($message, $responseCode); + break; + case 403: + throw new SignalAuthenticationException($this->apiKey, $this->apiSecret, null, $e); + break; + case 404: + $message = 'The client specified by the connectionId property is not connected to the session.'; + throw new SignalConnectionException($message, $responseCode); + break; + case 413: + $message = 'The type string exceeds the maximum length (128 bytes), or the data string exceeds the maximum size (8 kB).'; + throw new SignalUnexpectedValueException($message, $responseCode); + break; + default: + break; + } + } + + private function handleForceDisconnectException($e) + { + $responseCode = $e->getResponse()->getStatusCode(); + switch($responseCode) { + case 400: + $message = 'One of the arguments — sessionId or connectionId — is invalid.'; + throw new ForceDisconnectUnexpectedValueException($message, $responseCode); + break; + case 403: + throw new ForceDisconnectAuthenticationException($this->apiKey, $this->apiSecret, null, $e); + break; + case 404: + $message = 'The client specified by the connectionId property is not connected to the session.'; + throw new ForceDisconnectConnectionException($message, $responseCode); + break; + default: + break; + } + } + private function isDebug() { return defined('OPENTOK_DEBUG'); diff --git a/src/OpenTok/Util/Validators.php b/src/OpenTok/Util/Validators.php index 3887f17d..6239fc0b 100755 --- a/src/OpenTok/Util/Validators.php +++ b/src/OpenTok/Util/Validators.php @@ -70,6 +70,16 @@ public static function validateConnectionId($connectionId) ); } } + + public static function validateSignalPayload($payload) + { + list($type, $data) = array_values($payload); + if(!is_string($data) || is_null($data || is_null($type))){ + throw new InvalidArgumentException( + 'Signal Payload cannot be null: '.print_r($payload, true) + ); + } + } public static function validateRole($role) { if (!Role::isValidValue($role)) { diff --git a/tests/OpenTok/OpenTokTest.php b/tests/OpenTok/OpenTokTest.php index 9ada9dba..a0eb771d 100644 --- a/tests/OpenTok/OpenTokTest.php +++ b/tests/OpenTok/OpenTokTest.php @@ -7,6 +7,8 @@ use OpenTok\OpenTok; use OpenTok\OpenTokTestCase; use OpenTok\Session; +use OpenTok\Stream; +use OpenTok\StreamList; use OpenTok\Role; use OpenTok\Layout; use OpenTok\MediaMode; @@ -973,6 +975,50 @@ public function testListsArchivesWithOffsetAndCount() $this->assertEquals('832641bf-5dbf-41a1-ad94-fea213e59a92', $archiveList->getItems()[0]->id); } + public function testListsArchivesWithSessionId() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => 'v2/project/APIKEY/archive/get_third' + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + // Act + $archiveList = $this->opentok->listArchives(0, null, $sessionId); + + // Assert + $this->assertCount(1, $this->historyContainer); + + $request = $this->historyContainer[0]['request']; + $this->assertEquals('GET', strtoupper($request->getMethod())); + $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); + $this->assertEquals('https', $request->getUri()->getScheme()); + + $authString = $request->getHeaderLine('X-OPENTOK-AUTH'); + $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); + + // TODO: test the dynamically built User Agent string + $userAgent = $request->getHeaderLine('User-Agent'); + $this->assertNotEmpty($userAgent); + $this->assertStringStartsWith('OpenTok-PHP-SDK/4.1.2-alpha.1', $userAgent); + + $this->assertInstanceOf('OpenTok\ArchiveList', $archiveList); + $this->assertEquals(2, $archiveList->totalCount()); + $this->assertEquals($sessionId, $archiveList->getItems()[0]->sessionId); + $this->assertEquals($sessionId, $archiveList->getItems()[1]->sessionId); + $this->assertEquals('b8f64de1-e218-4091-9544-4cbf369fc238', $archiveList->getItems()[0]->id); + $this->assertEquals('832641bf-5dbf-41a1-ad94-fea213e59a92', $archiveList->getItems()[1]->id); + } + /** * @expectedException InvalidArgumentException */ @@ -1023,9 +1069,7 @@ public function testForceDisconnect() 'code' => 204 ]]); - // This sessionId was generated using a different apiKey, but this method doesn't do any - // decoding to check, so its fine. - $sessionId = '2_MX44NTQ1MTF-flR1ZSBOb3YgMTIgMDk6NDA6NTkgUFNUIDIwMTN-MC43NjU0Nzh-'; + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; $connectionId = '063e72a4-64b4-43c8-9da5-eca071daab89'; @@ -1056,6 +1100,25 @@ public function testForceDisconnect() $this->assertTrue($success); } + + public function testForceDisconnectConnectionException() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 404 + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + + $connectionId = '063e72a4-64b4-43c8-9da5-eca071daab89'; + + $this->expectException('OpenTok\Exception\ForceDisconnectConnectionException'); + + // Act + $this->opentok->forceDisconnect($sessionId, $connectionId); + + } + public function testStartsBroadcast() { // Arrange @@ -1313,6 +1376,45 @@ public function testUpdatesStreamLayoutClassList() $this->assertStringStartsWith('OpenTok-PHP-SDK/4.1.2-alpha.1', $userAgent); } + public function testGetStream() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/session/SESSIONID/stream/STREAMID/get' + ]]); + + $sessionId = 'SESSIONID'; + $streamId = '8b732909-0a06-46a2-8ea8-074e64d43422'; + + // Act + $streamData = $this->opentok->getStream($sessionId, $streamId); + // Assert + $this->assertCount(1, $this->historyContainer); + + $request = $this->historyContainer[0]['request']; + $this->assertEquals('GET', strtoupper($request->getMethod())); + $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/stream/'.$streamId, $request->getUri()->getPath()); + $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); + $this->assertEquals('https', $request->getUri()->getScheme()); + + $authString = $request->getHeaderLine('X-OPENTOK-AUTH'); + $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); + + $this->assertInstanceOf('OpenTok\Stream', $streamData); + $this->assertNotNull($streamData->id); + $this->assertNotNull($streamData->name); + $this->assertNotNull($streamData->videoType); + $this->assertNotNull($streamData->layoutClassList); + + $userAgent = $request->getHeaderLine('User-Agent'); + $this->assertNotEmpty($userAgent); + $this->assertStringStartsWith('OpenTok-PHP-SDK/4.1.2-alpha.1', $userAgent); + } + public function testSipCall() { @@ -1442,5 +1544,201 @@ public function testSipCallFrom() $body = json_decode($request->getBody()); $this->assertEquals($from, $body->sip->from); } + + public function testSignalData() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 204 + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + $payload = array( + 'data' => 'apple', + 'type' => 'signal type sample' + ); + + // Act + $this->opentok->signal($sessionId, $payload); + + // Assert + $this->assertCount(1, $this->historyContainer); + + $request = $this->historyContainer[0]['request']; + $this->assertEquals('POST', strtoupper($request->getMethod())); + $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/signal', $request->getUri()->getPath()); + $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); + $this->assertEquals('https', $request->getUri()->getScheme()); + + $authString = $request->getHeaderLine('X-OPENTOK-AUTH'); + $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); + + // TODO: test the dynamically built User Agent string + $userAgent = $request->getHeaderLine('User-Agent'); + $this->assertNotEmpty($userAgent); + $this->assertStringStartsWith('OpenTok-PHP-SDK/4.1.2-alpha.1', $userAgent); + + $body = json_decode($request->getBody()); + $this->assertEquals('apple', $body->data); + $this->assertEquals('signal type sample', $body->type); + } + + public function testSignalWithConnectionId() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 204 + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + $connectionId = 'da9cb410-e29b-4c2d-ab9e-fe65bf83fcaf'; + $payload = array( + 'type' => 'rest', + 'data' => 'random message' + ); + + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + // Act + $this->opentok->signal($sessionId, $payload, $connectionId); + + $this->assertCount(1, $this->historyContainer); + + $request = $this->historyContainer[0]['request']; + $this->assertEquals('POST', strtoupper($request->getMethod())); + $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/connection/'.$connectionId.'/signal', $request->getUri()->getPath()); + $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); + $this->assertEquals('https', $request->getUri()->getScheme()); + + $authString = $request->getHeaderLine('X-OPENTOK-AUTH'); + $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); + + // TODO: test the dynamically built User Agent string + $userAgent = $request->getHeaderLine('User-Agent'); + $this->assertNotEmpty($userAgent); + $this->assertStringStartsWith('OpenTok-PHP-SDK/4.1.2-alpha.1', $userAgent); + + $body = json_decode($request->getBody()); + $this->assertEquals('random message', $body->data); + $this->assertEquals('rest', $body->type); + } + + public function testSignalWithEmptyPayload() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 204 + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + $payload = array(); + + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + // Act + try { + $this->opentok->signal($sessionId, $payload); + } catch (\Exception $e) { + } + } + + public function testSignalConnectionException() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 404 + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + $connectionId = 'da9cb410-e29b-4c2d-ab9e-fe65bf83fcaf'; + $payload = array( + 'type' => 'rest', + 'data' => 'random message' + ); + + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + $this->expectException('OpenTok\Exception\SignalConnectionException'); + // Act + $this->opentok->signal($sessionId, $payload, $connectionId); + } + + public function testSignalUnexpectedValueException() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 413 + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + $connectionId = 'da9cb410-e29b-4c2d-ab9e-fe65bf83fcaf'; + $payload = array( + 'type' => 'rest', + 'data' => 'more than 128 bytes' + ); + + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + $this->expectException('OpenTok\Exception\SignalUnexpectedValueException'); + + // Act + $this->opentok->signal($sessionId, $payload, $connectionId); + + } + + public function testListStreams() + { + // Arrange + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/session/SESSIONID/stream/get' + ]]); + + $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'; + $bogusApiKey = '12345678'; + $bogusApiSecret = '0123456789abcdef0123456789abcdef0123456789'; + + $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); + + // Act + $streamList = $this->opentok->listStreams($sessionId); + + // Assert + $this->assertCount(1, $this->historyContainer); + + $request = $this->historyContainer[0]['request']; + $this->assertEquals('GET', strtoupper($request->getMethod())); + $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/stream/', $request->getUri()->getPath()); + $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); + $this->assertEquals('https', $request->getUri()->getScheme()); + + $authString = $request->getHeaderLine('X-OPENTOK-AUTH'); + $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); + + // TODO: test the dynamically built User Agent string + $userAgent = $request->getHeaderLine('User-Agent'); + $this->assertNotEmpty($userAgent); + $this->assertStringStartsWith('OpenTok-PHP-SDK/4.1.2-alpha.1', $userAgent); + + $this->assertInstanceOf('OpenTok\StreamList', $streamList); + + } + } /* vim: set ts=4 sw=4 tw=100 sts=4 et :*/ diff --git a/tests/OpenTok/SessionTest.php b/tests/OpenTok/SessionTest.php index a178d2db..e1b5631c 100644 --- a/tests/OpenTok/SessionTest.php +++ b/tests/OpenTok/SessionTest.php @@ -1,12 +1,20 @@ API_KEY = defined('API_KEY') ? API_KEY : '12345678'; @@ -21,6 +36,41 @@ public function setUp() $this->opentok = new OpenTok($this->API_KEY, $this->API_SECRET); } + + private function setupOTWithMocks($mocks) + { + $this->API_KEY = defined('API_KEY') ? API_KEY : '12345678'; + $this->API_SECRET = defined('API_SECRET') ? API_SECRET : '0123456789abcdef0123456789abcdef0123456789'; + + if (is_array($mocks)) { + $responses = TestHelpers::mocksToResponses($mocks, self::$mockBasePath); + } else { + $responses = []; + } + + $mock = new MockHandler($responses); + $handlerStack = HandlerStack::create($mock); + $clientOptions = [ + 'handler' => $handlerStack + ]; + + $this->client = new Client(); + $this->client->configure( + $this->API_KEY, + $this->API_SECRET, + 'https://api.opentok.com', + $clientOptions + ); + + // Push history onto handler stack *after* configuring client to + // ensure auth header is added before history handler is invoked + $this->historyContainer = []; + $history = Middleware::history($this->historyContainer); + $handlerStack->push($history); + + $this->opentok = new OpenTok($this->API_KEY, $this->API_SECRET, array('client' => $this->client)); + } + public function testSessionWithId() { $sessionId = 'SESSIONID'; @@ -152,5 +202,6 @@ public function testGeneratesToken() $this->assertNotEmpty($decodedToken['sig']); $this->assertEquals(hash_hmac('sha1', $decodedToken['dataString'], $bogusApiSecret), $decodedToken['sig']); } + } /* vim: set ts=4 sw=4 tw=100 sts=4 et :*/ diff --git a/tests/mock/v2/project/APIKEY/archive/get_third b/tests/mock/v2/project/APIKEY/archive/get_third new file mode 100644 index 00000000..03d6e749 --- /dev/null +++ b/tests/mock/v2/project/APIKEY/archive/get_third @@ -0,0 +1,26 @@ +{ + "count" : 2, + "items" : [ { + "createdAt" : 1394396753000, + "duration" : 24, + "id" : "b8f64de1-e218-4091-9544-4cbf369fc238", + "name" : "showtime again", + "partnerId" : 12345678, + "reason" : "", + "sessionId" : "1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI", + "size" : 2227849, + "status" : "available", + "url" : "http://tokbox.com.archive2.s3.amazonaws.com/854511%2Fb8f64de1-e218-4091-9544-4cbf369fc238%2Farchive.mp4?Expires=1394397391&AWSAccessKeyId=AKIAI6LQCPIXYVWCQV6Q&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + }, { + "createdAt" : 1394321113000, + "duration" : 1294, + "id" : "832641bf-5dbf-41a1-ad94-fea213e59a92", + "name" : "showtime", + "partnerId" : 12345678, + "reason" : "", + "sessionId" : "1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI", + "size" : 42165242, + "status" : "available", + "url" : "http://tokbox.com.archive2.s3.amazonaws.com/854511%2F832641bf-5dbf-41a1-ad94-fea213e59a92%2Farchive.mp4?Expires=1394397391&AWSAccessKeyId=AKIAI6LQCPIXYVWCQV6Q&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + } ] +} diff --git a/tests/mock/v2/project/APIKEY/session/SESSIONID/stream/STREAMID/get b/tests/mock/v2/project/APIKEY/session/SESSIONID/stream/STREAMID/get new file mode 100644 index 00000000..95de0092 --- /dev/null +++ b/tests/mock/v2/project/APIKEY/session/SESSIONID/stream/STREAMID/get @@ -0,0 +1,6 @@ +{ + "id":"8b732909-0a06-46a2-8ea8-074e64d43422", + "videoType":"camera", + "name":"no name", + "layoutClassList":["foo"] +} diff --git a/tests/mock/v2/project/APIKEY/session/SESSIONID/stream/get b/tests/mock/v2/project/APIKEY/session/SESSIONID/stream/get new file mode 100644 index 00000000..4446cf5a --- /dev/null +++ b/tests/mock/v2/project/APIKEY/session/SESSIONID/stream/get @@ -0,0 +1,17 @@ +{ + "count": 2 + "items": [ + { + "id": "8b732909-0a06-46a2-8ea8-074e64d43422", + "videoType": "camera", + "name": "item 1", + "layoutClassList": ["full"] + }, + { + "id": "ab732909-0a06-46a2-8ea8-074e64d43412", + "videoType": "screen", + "name": "item 2", + "layoutClassList": ["full"] + }, + ] +}