Skip to content

Commit ebb4029

Browse files
committed
Merge pull request #6 from clue/collector
Add Collector to process a list of user-generated events
2 parents c4256f6 + c2e8921 commit ebb4029

4 files changed

Lines changed: 203 additions & 0 deletions

File tree

example/peers.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
use Clue\React\Ami\Factory;
4+
use Clue\React\Ami\Client;
5+
use Clue\React\Ami\Api;
6+
use Clue\React\Ami\Collector;
7+
use Clue\React\Ami\Protocol\Collection;
8+
9+
require __DIR__ . '/../vendor/autoload.php';
10+
11+
$loop = React\EventLoop\Factory::create();
12+
$factory = new Factory($loop);
13+
14+
$target = isset($argv[1]) ? $argv[1] : 'name:password@localhost';
15+
16+
$factory->createClient($target)->then(function (Client $client) use ($loop) {
17+
echo 'Successfully connected' . PHP_EOL;
18+
19+
$collector = new Collector($client);
20+
21+
$collector->sipPeers()->then(function (Collection $collection) {
22+
var_dump('result', $collection);
23+
$peers = $collection->getEntryEvents();
24+
25+
echo 'found ' . count($peers) . ' peers' . PHP_EOL;
26+
});
27+
}, 'var_dump');
28+
29+
$loop->run();

src/Collector.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Clue\React\Ami;
4+
5+
use Clue\React\Ami\Client;
6+
use Clue\React\Ami\Protocol\Response;
7+
use Clue\React\Ami\Protocol\Event;
8+
use Clue\React\Ami\Protocol\Collection;
9+
use React\Promise\Deferred;
10+
11+
class Collector
12+
{
13+
private $client;
14+
15+
public function __construct(Client $client)
16+
{
17+
$this->client = $client;
18+
}
19+
20+
public function coreShowChannels()
21+
{
22+
return $this->collectEvents('CoreShowChannels', 'CoreShowChannelsComplete');
23+
}
24+
25+
public function sipPeers()
26+
{
27+
return $this->collectEvents('SIPPeers', 'PeerlistComplete');
28+
}
29+
30+
public function agents()
31+
{
32+
return $this->collectEvents('Agents', 'AgentsComplete');
33+
}
34+
35+
private function collectEvents($command, $expectedEndEvent)
36+
{
37+
$req = $this->client->createAction($command);
38+
$ret = $this->client->request($req);
39+
$id = $req->getActionId();
40+
41+
$deferred = new Deferred();
42+
43+
// collect all intermediary channel events with this action ID
44+
$collected = array();
45+
$collector = function (Event $event) use ($id, &$collected, $deferred, $expectedEndEvent) {
46+
if ($event->getActionId() === $id) {
47+
$collected []= $event;
48+
49+
if ($event->getName() === $expectedEndEvent) {
50+
$deferred->resolve($collected);
51+
}
52+
}
53+
};
54+
$this->client->on('event', $collector);
55+
56+
// unregister collector if client fails
57+
$client = $this->client;
58+
$unregister = function () use ($client, $collector) {
59+
$client->removeListener('event', $collector);
60+
};
61+
$ret->then(null, $unregister);
62+
63+
// stop waiting for events
64+
$deferred->promise()->then($unregister);
65+
66+
return $ret->then(function (Response $response) use ($deferred) {
67+
// final result has been received => merge all intermediary channel events
68+
return $deferred->promise()->then(function ($collected) use ($response) {
69+
$last = array_pop($collected);
70+
return new Collection($response, $collected, $last);
71+
});
72+
});
73+
}
74+
}

src/Protocol/Collection.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Clue\React\Ami\Protocol;
4+
5+
use UnexpectedValueException;
6+
7+
class Collection extends Message
8+
{
9+
private $response;
10+
private $entryEvents;
11+
private $completeEvent;
12+
13+
public function __construct(Response $response, array $entryEvents, Event $completeEvent)
14+
{
15+
// if ($response->getPart('EventItems') !== count($events)) {
16+
// var_dump($response, $events);
17+
// throw new UnexpectedValueException('Expected ' . $response->getPart('EventItems') . ' entries, but collected ' . count($events));
18+
// }
19+
20+
$this->parts = $response->getParts();
21+
$this->response = $response;
22+
$this->entryEvents = $entryEvents;
23+
$this->completeEvent = $completeEvent;
24+
}
25+
26+
public function getResponse()
27+
{
28+
return $this->response;
29+
}
30+
31+
public function getEntryEvents()
32+
{
33+
return $this->entryEvents;
34+
}
35+
36+
public function getCompleteEvent()
37+
{
38+
return $this->completeEvent;
39+
}
40+
}

tests/CollectorTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
use Clue\React\Ami\Collector;
4+
use Clue\React\Ami\Protocol\Collection;
5+
use Clue\React\Ami\Protocol\Response;
6+
use Clue\React\Ami\Client;
7+
use Clue\React\Ami\Protocol\Event;
8+
use Clue\React\Ami\Protocol\Action;
9+
10+
class CollectorTest extends TestCase
11+
{
12+
public function testCollectingSIPEvents()
13+
{
14+
$client = $this->createClientMock();
15+
16+
// expect a single outgoing action request (and mock its ID)
17+
$client->expects($this->once())
18+
->method('createAction')
19+
->with($this->equalTo('SIPPeers'), $this->equalTo(array()))
20+
->will($this->returnValue(new Action('SIPPeers', array('ActionID' => '123'))));
21+
22+
$collector = new Collector($client);
23+
24+
$promise = $collector->sipPeers();
25+
26+
// save resolved result for comparisions
27+
$resolved = null;
28+
$promise->then(function($result) use (&$resolved) {
29+
$resolved = $result;
30+
});
31+
32+
// should not start out resolved
33+
$this->assertNull($resolved);
34+
35+
$response = new Response(array('Response' => 'Success', 'ActionID' => '123'));
36+
37+
$client->handleMessage($response);
38+
$client->handleMessage(new Event('PeerEntry', array('ActionID' => '123')));
39+
$client->handleMessage(new Event('PeerEntry', array('ActionID' => '123')));
40+
41+
$this->assertNull($resolved);
42+
43+
$client->handleMessage(new Event('PeerlistComplete', array('EventList' => 'complete', 'ListItems' => '2', 'ActionID' => '123')));
44+
45+
$this->assertNotNull($resolved);
46+
47+
$promise->then(
48+
$this->expectCallableOnce()
49+
);
50+
}
51+
52+
private function createClientMock()
53+
{
54+
$stream = $this->getMockBuilder('React\Stream\Stream')->disableOriginalConstructor()->getMock();
55+
56+
$client = $this->getMockBuilder('Clue\React\Ami\Client')->setMethods(array('createAction'))->setConstructorArgs(array($stream))->getMock();
57+
58+
return $client;
59+
}
60+
}

0 commit comments

Comments
 (0)