Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify Collection by extending Response and merging Collector into ActionSender #41

Merged
merged 2 commits into from
Sep 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 87 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ monitor the status of subscribers, channels or queues.
* [getActionId()](#getactionid)
* [Response](#response)
* [getCommandOutput()](#getcommandoutput)
* [Collection](#collection)
* [getEntryEvents()](#getentryevents)
* [getCompleteEvent()](#getcompleteevent)
* [Action](#action)
* [Event](#event)
* [getName()](#getname)
Expand Down Expand Up @@ -205,9 +208,17 @@ $sender = new ActionSender($client);
All public methods resemble their respective AMI actions.

```php
$sender->ping()->then(function (Response $response) {
// response received for ping action
});
$sender->login($name, $pass);
$sender->logoff();
$sender->ping();
$sender->command($command);
$sender->events($eventMask);

$sender->coreShowChannels();
$sender->sipPeers();
$sender->agents();

// many more…
```

Listing all available actions is out of scope here, please refer to the [class outline](src/ActionSender.php).
Expand Down Expand Up @@ -237,6 +248,10 @@ $sender->ping()->then(
});
```

All actions resolve with a [`Response`](#response) object on success,
some actions are documented to return the specialized [`Collection`](#collection)
object to contain a list of entries.

#### Custom actions

Using the `ActionSender` is not strictly necessary, but is the recommended way to execute common actions.
Expand All @@ -247,7 +262,8 @@ A PR that updates the `ActionSender` is very much appreciated :)

### Message

The `Message` is an abstract base class for the [`Response`](#response), [`Action`](#action) and [`Event`](#event) value objects.
The `Message` is an abstract base class for the [`Response`](#response),
[`Action`](#action) and [`Event`](#event) value objects.
It provides a common interface for these three message types.

Each `Message` consists of any number of fields with each having a name and one or multiple values.
Expand All @@ -272,12 +288,12 @@ The `getFields(): array` method can be used to get an array of all fields.
The `getActionId(): string` method can be used to get the unique action ID of this message.
This is a shortcut to get the value of the "ActionID" field.

#### Response
### Response

The `Response` value object represents the incoming response received from the AMI.
It shares all properties of the [`Message`](#message) parent class.

##### getCommandOutput()
#### getCommandOutput()

The `getCommandOutput(): ?string` method can be used to get the resulting output of
a "command" [`Action`](#action).
Expand All @@ -290,17 +306,79 @@ $sender->command('help')->then(function (Response $response) {
});
```

#### Action
### Collection

The `Collection` value object represents an incoming response received from the AMI
for certain actions that return a list of entries.
It shares all properties of the [`Response`](#response) parent class.

You can access the `Collection` like a normal `Response` in order to access
the leading `Response` for this collection or you can use the below methods
to access the list entries and completion event.

```
Action: CoreShowChannels

Response: Success
EventList: start
Message: Channels will follow

Event: CoreShowChannel
Channel: SIP / 123
ChannelState: 6
ChannelStateDesc: Up

Event: CoreShowChannel
Channel: SIP / 456
ChannelState: 6
ChannelStateDesc: Up

Event: CoreShowChannel
Channel: SIP / 789
ChannelState: 6
ChannelStateDesc: Up

Event: CoreShowChannelsComplete
EventList: Complete
ListItems: 3
```

#### getEntryEvents()

The `getEntryEvents(): Event[]` method can be used to get the list of all
intermediary `Event` objects where each entry represents a single entry in the
collection.

```php
foreach ($collection->getEntryEvents() as $entry) {
/* @var $entry Event */
echo $entry->getFieldValue('Channel') . PHP_EOL;
}
```

#### getCompleteEvent()

The `getCompleteEvent(): Event` method can be used to get the trailing
`Event` that completes this collection.

```php
echo $collection->getCompleteEvent()->getFieldValue('ListItems') . PHP_EOL;
```

### Action

The `Action` value object represents an outgoing action message to be sent to the AMI.
It shares all properties of the [`Message`](#message) parent class.

#### Event
### Event

The `Event` value object represents the incoming event received from the AMI.
It shares all properties of the [`Message`](#message) parent class.

##### getName()
#### getName()

The `getName(): ?string` method can be used to get the name of the event.
This is a shortcut to get the value of the "Event" field.
Expand Down
7 changes: 3 additions & 4 deletions examples/peers.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<?php

use Clue\React\Ami\Factory;
use Clue\React\Ami\Client;
use Clue\React\Ami\ActionSender;
use Clue\React\Ami\Collector;
use Clue\React\Ami\Client;
use Clue\React\Ami\Factory;
use Clue\React\Ami\Protocol\Collection;

require __DIR__ . '/../vendor/autoload.php';
Expand All @@ -16,7 +15,7 @@
$factory->createClient($target)->then(function (Client $client) use ($loop) {
echo 'Successfully connected' . PHP_EOL;

$collector = new Collector($client);
$collector = new ActionSender($client);

$collector->sipPeers()->then(function (Collection $collection) {
var_dump('result', $collection);
Expand Down
82 changes: 69 additions & 13 deletions src/ActionSender.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace Clue\React\Ami;

use Clue\React\Ami\Client;
use Clue\React\Ami\Protocol\Response;
use Clue\React\Ami\Protocol\Action;
use UnexpectedValueException;
use Clue\React\Ami\Protocol\Collection;
use Clue\React\Ami\Protocol\Event;
use Clue\React\Ami\Protocol\Response;
use React\Promise\Deferred;

class ActionSender
{
Expand Down Expand Up @@ -39,11 +39,6 @@ public function ping()
return $this->request('Ping');
}

public function coreShowChannels()
{
return $this->request('CoreShowChannels');
}

public function command($command)
{
return $this->request('Command', array('Command' => $command));
Expand All @@ -62,11 +57,6 @@ public function events($eventMask)
return $this->request('Events', array('EventMask' => $eventMask));
}

public function sipPeers()
{
return $this->request('SIPPeers');
}

public function sipShowPeer($peerName)
{
return $this->request('SIPshowpeer', array('Peer' => $peerName));
Expand Down Expand Up @@ -97,6 +87,32 @@ public function getConfig($filename, $category = null)
return $this->request('GetConfig', array('Filename' => $filename, 'Category' => $category));
}

/**
* @return \React\Promise\PromiseInterface Promise<Collection> collection with "Event: CoreShowChannel"
*/
public function coreShowChannels()
{
return $this->collectEvents('CoreShowChannels', 'CoreShowChannelsComplete');
}

/**
*
* @return \React\Promise\PromiseInterface Promise<Collection> collection with "Event: PeerEntry"
*/
public function sipPeers()
{
return $this->collectEvents('SIPPeers', 'PeerlistComplete');
}

/**
*
* @return \React\Promise\PromiseInterface Promise<Collection> collection with "Event: Agents"
*/
public function agents()
{
return $this->collectEvents('Agents', 'AgentsComplete');
}

private function boolParam($value)
{
if ($value === true) {
Expand All @@ -112,4 +128,44 @@ private function request($name, array $args = array())
{
return $this->client->request($this->client->createAction($name, $args));
}

private function collectEvents($command, $expectedEndEvent)
{
$req = $this->client->createAction($command);
$ret = $this->client->request($req);
$id = $req->getActionId();

$deferred = new Deferred();

// collect all intermediary channel events with this action ID
$collected = array();
$collector = function (Event $event) use ($id, &$collected, $deferred, $expectedEndEvent) {
if ($event->getActionId() === $id) {
$collected []= $event;

if ($event->getName() === $expectedEndEvent) {
$deferred->resolve($collected);
}
}
};
$this->client->on('event', $collector);

// unregister collector if client fails
$client = $this->client;
$unregister = function () use ($client, $collector) {
$client->removeListener('event', $collector);
};
$ret->then(null, $unregister);

// stop waiting for events
$deferred->promise()->then($unregister);

return $ret->then(function (Response $response) use ($deferred) {
// final result has been received => merge all intermediary channel events
return $deferred->promise()->then(function ($collected) use ($response) {
$last = array_pop($collected);
return new Collection($response, $collected, $last);
});
});
}
}
74 changes: 0 additions & 74 deletions src/Collector.php

This file was deleted.

11 changes: 1 addition & 10 deletions src/Protocol/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,18 @@

namespace Clue\React\Ami\Protocol;

use UnexpectedValueException;

class Collection extends Message
class Collection extends Response
{
private $response;
private $entryEvents;
private $completeEvent;

public function __construct(Response $response, array $entryEvents, Event $completeEvent)
{
$this->fields = $response->getFields();
$this->response = $response;
$this->entryEvents = $entryEvents;
$this->completeEvent = $completeEvent;
}

public function getResponse()
{
return $this->response;
}

public function getEntryEvents()
{
return $this->entryEvents;
Expand Down
Loading