From f28d94f6a6897d260595e3b0ca995fbe93c9e68f Mon Sep 17 00:00:00 2001 From: Eric Salter Date: Tue, 10 Mar 2015 11:02:53 -0400 Subject: [PATCH 1/4] Add Hipchat V2 support Adds support for Hipchat's V2 API by way of setting the version flag. V1 paths should remain unchanged. A setVersion() method is provided for convenience in case one does not want to override all of the default parameters. --- src/Monolog/Handler/HipChatHandler.php | 74 +++++++++++++++----- tests/Monolog/Handler/HipChatHandlerTest.php | 61 +++++++++++++++- 2 files changed, 115 insertions(+), 20 deletions(-) diff --git a/src/Monolog/Handler/HipChatHandler.php b/src/Monolog/Handler/HipChatHandler.php index 185e86e0..ee8e4b20 100644 --- a/src/Monolog/Handler/HipChatHandler.php +++ b/src/Monolog/Handler/HipChatHandler.php @@ -21,12 +21,23 @@ use Monolog\Logger; * Room - HipChat Room Id or name, where messages are sent * Name - Name used to send the message (from) * notify - Should the message trigger a notification in the clients + * version - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2) * * @author Rafael Dohms * @see https://www.hipchat.com/docs/api */ class HipChatHandler extends SocketHandler { + /** + * Use API version 1 + */ + const API_V1 = 'v1'; + + /** + * Use API version v2 + */ + const API_V2 = 'v2'; + /** * The maximum allowed length for the name used in the "from" field. */ @@ -68,22 +79,24 @@ class HipChatHandler extends SocketHandler private $host; /** - * @param string $token HipChat API Token - * @param string $room The room that should be alerted of the message (Id or Name) - * @param string $name Name used in the "from" field - * @param bool $notify Trigger a notification in clients or not - * @param 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 bool $useSSL Whether to connect via SSL. - * @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) - * @param string $host The HipChat server hostname. + * @var string */ - public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com') - { - if (!$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { - throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); - } + private $version; + /** + * @param string $token HipChat API Token + * @param string $room The room that should be alerted of the message (Id or Name) + * @param string $name Name used in the "from" field. Not used for v2 + * @param bool $notify Trigger a notification in clients or not + * @param 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 bool $useSSL Whether to connect via SSL. + * @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) + * @param string $host The HipChat server hostname. + * @param string $version The HipChat API version (default HipChatHandler::API_V1) + */ + public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) + { $connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80'; parent::__construct($connectionString, $level, $bubble); @@ -93,6 +106,22 @@ class HipChatHandler extends SocketHandler $this->room = $room; $this->format = $format; $this->host = $host; + + $this->setVersion($version); + } + + /** + * Convenience method for setting the API version. Valid values are + * - HipChatHandler::API_V1 + * - HipChatHandler::API_V2 + * @param $version string the API version to use + */ + public function setVersion($version) { + if ($version == self::API_V1 && !$this->validateStringLength($this->name, static::MAXIMUM_NAME_LENGTH)) { + throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); + } + + $this->version = $version; } /** @@ -117,14 +146,18 @@ class HipChatHandler extends SocketHandler private function buildContent($record) { $dataArray = array( - 'from' => $this->name, - 'room_id' => $this->room, 'notify' => $this->notify, 'message' => $record['formatted'], 'message_format' => $this->format, 'color' => $this->getAlertColor($record['level']), ); + // if we are using the legacy API then we need to send some additional information + if ($this->version == self::API_V1) { + $dataArray['room_id'] = $this->room; + $dataArray['from'] = $this->name; + } + return http_build_query($dataArray); } @@ -136,7 +169,14 @@ class HipChatHandler extends SocketHandler */ private function buildHeader($content) { - $header = "POST /v1/rooms/message?format=json&auth_token=".$this->token." HTTP/1.1\r\n"; + if ($this->version == self::API_V1) { + $header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n"; + } else { + // needed for rooms with special (spaces, etc) characters in the name + $room = rawurlencode($this->room); + $header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n"; + } + $header .= "Host: {$this->host}\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . strlen($content) . "\r\n"; diff --git a/tests/Monolog/Handler/HipChatHandlerTest.php b/tests/Monolog/Handler/HipChatHandlerTest.php index 49f1dfbd..bf15e547 100644 --- a/tests/Monolog/Handler/HipChatHandlerTest.php +++ b/tests/Monolog/Handler/HipChatHandlerTest.php @@ -35,6 +35,18 @@ class HipChatHandlerTest extends TestCase return $content; } + public function testCanSetVersionAfterCreate() { + $this->createHandler(); + $this->handler->setVersion('v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: api.hipchat.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + public function testWriteCustomHostHeader() { $this->createHandler('myToken', 'room1', 'Monolog', false, 'hipchat.foo.bar'); @@ -47,12 +59,50 @@ class HipChatHandlerTest extends TestCase return $content; } + public function testWriteV2() { + $this->createHandler('myToken', 'room1', 'Monolog', false, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + public function testRoomSpaces() { + $this->createHandler('myToken', 'room name', 'Monolog', false, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room%20name\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + /** * @depends testWriteHeader */ public function testWriteContent($content) { - $this->assertRegexp('/from=Monolog&room_id=room1¬ify=0&message=test1&message_format=text&color=red$/', $content); + $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); + } + + /** + * @depends testWriteV2 + */ + public function testWriteContentV2($content) + { + $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red$/', $content); + } + + /** + * @depends testCanSetVersionAfterCreate + */ + public function testWriteContentV2AfterCreate($content) + { + $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red$/', $content); } public function testWriteWithComplexMessage() @@ -141,9 +191,9 @@ class HipChatHandlerTest extends TestCase ); } - private function createHandler($token = 'myToken', $room = 'room1', $name = 'Monolog', $notify = false, $host = 'api.hipchat.com') + private function createHandler($token = 'myToken', $room = 'room1', $name = 'Monolog', $notify = false, $host = 'api.hipchat.com', $version = 'v1') { - $constructorArgs = array($token, $room, $name, $notify, Logger::DEBUG, true, true, 'text', $host); + $constructorArgs = array($token, $room, $name, $notify, Logger::DEBUG, true, true, 'text', $host, $version); $this->res = fopen('php://memory', 'a'); $this->handler = $this->getMock( '\Monolog\Handler\HipChatHandler', @@ -175,4 +225,9 @@ class HipChatHandlerTest extends TestCase { $hipChatHandler = new \Monolog\Handler\HipChatHandler('token', 'room', 'SixteenCharsHere'); } + + public function testCreateWithTooLongNameV2() { + // creating a handler with too long of a name but using the v2 api doesn't matter. + $hipChatHandler = new \Monolog\Handler\HipChatHandler('token', 'room', 'SixteenCharsHere', false, Logger::CRITICAL, true, true, 'test', 'api.hipchat.com', 'v2'); + } } From 6e8c4a149c085ec2fe74778c61bf941e4fc611bf Mon Sep 17 00:00:00 2001 From: Eric Salter Date: Tue, 10 Mar 2015 13:54:29 -0400 Subject: [PATCH 2/4] Remove setVersion() method Removed per request --- src/Monolog/Handler/HipChatHandler.php | 19 ++++--------------- tests/Monolog/Handler/HipChatHandlerTest.php | 20 -------------------- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/Monolog/Handler/HipChatHandler.php b/src/Monolog/Handler/HipChatHandler.php index ee8e4b20..6b0fa985 100644 --- a/src/Monolog/Handler/HipChatHandler.php +++ b/src/Monolog/Handler/HipChatHandler.php @@ -97,6 +97,10 @@ class HipChatHandler extends SocketHandler */ public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) { + if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { + throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); + } + $connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80'; parent::__construct($connectionString, $level, $bubble); @@ -106,21 +110,6 @@ class HipChatHandler extends SocketHandler $this->room = $room; $this->format = $format; $this->host = $host; - - $this->setVersion($version); - } - - /** - * Convenience method for setting the API version. Valid values are - * - HipChatHandler::API_V1 - * - HipChatHandler::API_V2 - * @param $version string the API version to use - */ - public function setVersion($version) { - if ($version == self::API_V1 && !$this->validateStringLength($this->name, static::MAXIMUM_NAME_LENGTH)) { - throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); - } - $this->version = $version; } diff --git a/tests/Monolog/Handler/HipChatHandlerTest.php b/tests/Monolog/Handler/HipChatHandlerTest.php index bf15e547..6b0e9767 100644 --- a/tests/Monolog/Handler/HipChatHandlerTest.php +++ b/tests/Monolog/Handler/HipChatHandlerTest.php @@ -35,18 +35,6 @@ class HipChatHandlerTest extends TestCase return $content; } - public function testCanSetVersionAfterCreate() { - $this->createHandler(); - $this->handler->setVersion('v2'); - $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); - fseek($this->res, 0); - $content = fread($this->res, 1024); - - $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: api.hipchat.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); - - return $content; - } - public function testWriteCustomHostHeader() { $this->createHandler('myToken', 'room1', 'Monolog', false, 'hipchat.foo.bar'); @@ -97,14 +85,6 @@ class HipChatHandlerTest extends TestCase $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red$/', $content); } - /** - * @depends testCanSetVersionAfterCreate - */ - public function testWriteContentV2AfterCreate($content) - { - $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red$/', $content); - } - public function testWriteWithComplexMessage() { $this->createHandler(); From 201bce80220c8bccd2bed5bd0db56c42897c2cea Mon Sep 17 00:00:00 2001 From: Roberto Araujo Date: Wed, 3 Jun 2015 22:21:16 -0300 Subject: [PATCH 3/4] Using the correct value for parameter 'notify' --- src/Monolog/Handler/HipChatHandler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Monolog/Handler/HipChatHandler.php b/src/Monolog/Handler/HipChatHandler.php index 6b0fa985..34d3437f 100644 --- a/src/Monolog/Handler/HipChatHandler.php +++ b/src/Monolog/Handler/HipChatHandler.php @@ -135,7 +135,9 @@ class HipChatHandler extends SocketHandler private function buildContent($record) { $dataArray = array( - 'notify' => $this->notify, + 'notify' => $this->version == self::API_V1 ? + ($this->notify ? 1 : 0) : + ($this->notify ? 'true' : 'false'), 'message' => $record['formatted'], 'message_format' => $this->format, 'color' => $this->getAlertColor($record['level']), From d8e7f9debf251faf6684cf836d6729caaa8f2b7d Mon Sep 17 00:00:00 2001 From: Roberto Araujo Date: Fri, 12 Jun 2015 16:52:46 -0300 Subject: [PATCH 4/4] Fix HipChatHandler tests --- tests/Monolog/Handler/HipChatHandlerTest.php | 31 ++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/Monolog/Handler/HipChatHandlerTest.php b/tests/Monolog/Handler/HipChatHandlerTest.php index 6b0e9767..ff773c98 100644 --- a/tests/Monolog/Handler/HipChatHandlerTest.php +++ b/tests/Monolog/Handler/HipChatHandlerTest.php @@ -37,7 +37,7 @@ class HipChatHandlerTest extends TestCase public function testWriteCustomHostHeader() { - $this->createHandler('myToken', 'room1', 'Monolog', false, 'hipchat.foo.bar'); + $this->createHandler('myToken', 'room1', 'Monolog', true, 'hipchat.foo.bar'); $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); fseek($this->res, 0); $content = fread($this->res, 1024); @@ -58,6 +58,17 @@ class HipChatHandlerTest extends TestCase return $content; } + public function testWriteV2Notify() { + $this->createHandler('myToken', 'room1', 'Monolog', true, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + public function testRoomSpaces() { $this->createHandler('myToken', 'room name', 'Monolog', false, 'hipchat.foo.bar', 'v2'); $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); @@ -77,12 +88,28 @@ class HipChatHandlerTest extends TestCase $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); } + /** + * @depends testWriteCustomHostHeader + */ + public function testWriteContentNotify($content) + { + $this->assertRegexp('/notify=1&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); + } + /** * @depends testWriteV2 */ public function testWriteContentV2($content) { - $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red$/', $content); + $this->assertRegexp('/notify=false&message=test1&message_format=text&color=red$/', $content); + } + + /** + * @depends testWriteV2Notify + */ + public function testWriteContentV2Notify($content) + { + $this->assertRegexp('/notify=true&message=test1&message_format=text&color=red$/', $content); } public function testWriteWithComplexMessage()