diff --git a/src/Monolog/Handler/FlowdockHandler.php b/src/Monolog/Handler/FlowdockHandler.php index 08820adf..2a52a9ff 100644 --- a/src/Monolog/Handler/FlowdockHandler.php +++ b/src/Monolog/Handler/FlowdockHandler.php @@ -11,7 +11,6 @@ namespace Monolog\Handler; -use Monolog\Formatter\LineFormatter; use Monolog\Logger; /** @@ -23,7 +22,7 @@ use Monolog\Logger; * @author Dominik Liebler * @see https://www.flowdock.com/api/push */ -class FlowdockHandler extends AbstractProcessingHandler +class FlowdockHandler extends SocketHandler { /** * @var string @@ -31,56 +30,72 @@ class FlowdockHandler extends AbstractProcessingHandler protected $apiToken; /** - * @param string $apiToken - * @param bool|int $level The minimum logging level at which this handler will be triggered - * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $apiToken + * @param bool|int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @throws MissingExtensionException if OpenSSL is missing */ public function __construct($apiToken, $level = Logger::DEBUG, $bubble = true) { - parent::__construct($level, $bubble); + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + + parent::__construct('ssl://api.flowdock.com:443', $level, $bubble); $this->apiToken = $apiToken; } /** + * {@inheritdoc} + * * @param array $record */ public function write(array $record) { - $uri = 'https://api.flowdock.com/v1/messages/team_inbox/' . $this->apiToken; + parent::write($record); - $tags = array( - '#logs', - '#' . strtolower($record['level_name']), - '#' . $record['channel'], - ); + $this->closeSocket(); + } - foreach ($record['extra'] as $key => $value) { - if ($key != 'requestIdent') { - $tags[] = '#' . $value; - } - } + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); - $data = array( - "subject" => sprintf("[%s] %s", $record['level_name'], $record['message']), - "content" => $record['message'], - "tags" => $tags - ); + return $this->buildHeader($content) . $content; + } - // add all extras - foreach ($record['extra'] as $key => $extra) { - $data[$key] = $extra; - } + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + return json_encode($record['formatted']['flowdock']); + } - $streamContext = stream_context_create(array( - 'http' => array( - 'method' => 'POST', - 'headers' => "Content-type: application/json\r\n", - 'content' => json_encode($data) - ) - )); + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; - $streamHandle = fopen($uri, 'r', false, $streamContext); - stream_get_contents($streamHandle); - fclose($streamHandle); + return $header; } } diff --git a/tests/Monolog/Handler/FlowdockHandlerTest.php b/tests/Monolog/Handler/FlowdockHandlerTest.php new file mode 100644 index 00000000..958de541 --- /dev/null +++ b/tests/Monolog/Handler/FlowdockHandlerTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FlowdockFormatter; +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @author Dominik Liebler + * @see https://www.hipchat.com/docs/api + */ +class FlowdockHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $res; + + /** + * @var FlowdockHandler + */ + private $handler; + + public function testWriteHeader() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v1\/messages\/team_inbox\/.* HTTP\/1.1\\r\\nHost: api.flowdock.com\\r\\nContent-Type: application\/json\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + /** + * @depends testWriteHeader + */ + public function testWriteContent($content) + { + $this->assertRegexp('/"source":"test_source"/', $content); + $this->assertRegexp('/"from_address":"source@test\.com"/', $content); + } + + private function createHandler($token = 'myToken') + { + $constructorArgs = array($token, Logger::DEBUG); + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\FlowdockHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $constructorArgs + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + + $this->handler->setFormatter(new FlowdockFormatter('test_source', 'source@test.com')); + } +}