Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Commit

  • Loading branch information...
commit c3f585f497f785188684d2745330fbd4de9b4377 0 parents
@davedevelopment authored
48 README.md
@@ -0,0 +1,48 @@
+Sismo Geckoboard Notifier
+=========================
+
+What is it?
+-----------
+
+I had a couple of hours to spare, so I thought I'd have a crack at creating a
+[Sismo](https://github.com/fabpot/Sismo) notifier for the first [Ibuildings
+Challenge](http://ibuildings.com/challenge) of 2012. It's a simple notifier that
+pushes a message up to a widget on your [Geckoboard](http://geckoboard.com)
+
+I've used a hacky way to send the HTTP POST in order to keep this dependency
+free.
+
+Usage
+-----
+
+First up, you need a Geckoboard account, one with the Push API enabled (I had to
+ask, I assume at some point it will be the default). Add a custom text widget to
+your board, select Push as the method and give it an API key. The system should
+provide you a URL to use for the widget.
+
+In your Sismo config
+
+``` php
+<?php
+$notifer = new Davedevelopment\Sismo\GeckoboardNotifier(
+ "your_api_key",
+ "your_widget_url"
+);
+
+```
+
+Try running sismo
+
+You can customise the display by passing a third parameter to the constructor,
+either a string or a callback that would take a `Sismo\Commit` instance
+
+Todo
+----
+
+* <del>Could optionally take a widget url rather than key, in case they change things</del>
+* <del>Take Buzz out</del>
+
+Copyright
+---------
+
+Copyright (c) 2012 Dave Marshall. See LICENCE for further details
14 composer.json
@@ -0,0 +1,14 @@
+{
+ "require": {
+ "php": ">=5.3.2"
+ },
+
+ "suggest": {
+ "sismo/sismo": "dev-master"
+ },
+
+ "autoload": {
+ "psr-0": { "Davedevelopment\\Sismo": "src/" }
+ }
+
+}
90 composer.lock
@@ -0,0 +1,90 @@
+{
+ "hash": "8f1210d3873394910fd7415297fa82aa",
+ "packages": [
+ {
+ "package": "pimple/pimple",
+ "version": "dev-master",
+ "source-reference": "321db91e49b6cf8cbeed58d8db662d40de96d2c3"
+ },
+ {
+ "package": "silex/silex",
+ "version": "dev-master",
+ "source-reference": "d230b294e118d2b2f392099cc2f249ea385b2640"
+ },
+ {
+ "package": "sismo/sismo",
+ "version": "dev-master",
+ "source-reference": "83d5467732a7013e9f618a6e0b49f16fbede32ac"
+ },
+ {
+ "package": "symfony/browser-kit",
+ "version": "dev-master",
+ "source-reference": "d02c0498db6a232828fccb547cc02340840fbef5"
+ },
+ {
+ "package": "symfony/class-loader",
+ "version": "dev-master",
+ "source-reference": "e5b63e811d167a0bac53434e44fecd86427839d7"
+ },
+ {
+ "package": "symfony/console",
+ "version": "dev-master",
+ "source-reference": "e93e53c05b11bd1870a0d74ec043b753605678fa"
+ },
+ {
+ "package": "symfony/css-selector",
+ "version": "dev-master",
+ "source-reference": "8d887d7fc25f7a0e189cac57de8d524c7b1ce465"
+ },
+ {
+ "package": "symfony/dom-crawler",
+ "version": "dev-master",
+ "source-reference": "8921dfcab04dc9dc0640376af3908479d85a413c"
+ },
+ {
+ "package": "symfony/event-dispatcher",
+ "version": "dev-master",
+ "source-reference": "183079978ea894de7dc3b167d2443192761974d5"
+ },
+ {
+ "package": "symfony/filesystem",
+ "version": "dev-master",
+ "source-reference": "a085980d8becf87271978a5516b52bc1834511fa"
+ },
+ {
+ "package": "symfony/finder",
+ "version": "dev-master",
+ "source-reference": "42aad00f75e02d05035528588a06d15a84806f3b"
+ },
+ {
+ "package": "symfony/http-foundation",
+ "version": "dev-master",
+ "source-reference": "f2c87e36e5f0c01195dc13e55ea5936c56a0da68"
+ },
+ {
+ "package": "symfony/http-kernel",
+ "version": "dev-master",
+ "source-reference": "2e12f0e93262542ae9b2269bc379e43107d6fe1c"
+ },
+ {
+ "package": "symfony/process",
+ "version": "dev-master",
+ "source-reference": "8b2aea5743c826b3f8e09388afdc2b35ccff8389"
+ },
+ {
+ "package": "symfony/routing",
+ "version": "dev-master",
+ "source-reference": "43f45fb77cb02e56740be5803ac8f8a690fb3e5b"
+ },
+ {
+ "package": "symfony/twig-bridge",
+ "version": "dev-master",
+ "source-reference": "7e32f82aa67fdda603477d59ec85ea5517edc480"
+ },
+ {
+ "package": "twig/twig",
+ "version": "1.6.0"
+ }
+ ],
+ "aliases": []
+}
36 phpunit.xml.dist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
+<phpunit
+ backupGlobals = "false"
+ backupStaticAttributes = "false"
+ colors = "true"
+ convertErrorsToExceptions = "true"
+ convertNoticesToExceptions = "true"
+ convertWarningsToExceptions = "true"
+ processIsolation = "false"
+ stopOnFailure = "false"
+ syntaxCheck = "false"
+ bootstrap = "vendor/.composer/autoload.php" >
+
+ <testsuites>
+ <testsuite name="Sismo Geckoboard Notifier Suite">
+ <directory>tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <!--
+ <logging>
+ <log type="coverage-clover" target="build/logs/clover.xml" />
+ <log type="coverage-html" target="build/coverage" title="Sismo Geckoboard Notifier" />
+ <log type="junit" target="build/logs/junit.xml" />
+ </logging>
+
+ <filter>
+ <whitelist>
+ <directory>src</directory>
+ </whitelist>
+ </filter>
+ -->
+
+</phpunit>
196 src/Davedevelopment/Sismo/GeckoboardNotifier.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Davedevelopment\Sismo;
+
+use Sismo\Notifier;
+use Sismo\Commit;
+use Buzz\Browser;
+
+class GeckoboardNotifier extends Notifier
+{
+ /**
+ * @var string
+ */
+ protected $apiKey;
+
+ /**
+ * @var string
+ */
+ protected $widgetKey;
+
+ /**
+ * @var String|Callable
+ */
+ protected $format = null;
+
+ /**
+ * @var callable
+ *
+ * Just for testing...
+ */
+ protected $poster = null;
+
+ /**
+ * Constructor
+ *
+ * @param string $apiKey
+ * @param string $widget
+ * @param string|callable $format
+ */
+ public function __construct($apiKey, $widget, $format = null)
+ {
+ $this->apiKey = $apiKey;
+ if (0 !== strpos($widget, 'http')) {
+ $widget = "https://push.geckoboard.com/v1/send/" . $widget;
+ }
+ $this->widgetUrl = $widget;
+
+ if ($format !== null) {
+ $this->setFormat($format);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function notify(Commit $commit)
+ {
+ $type = $commit->getStatus() == 'failed' ? 1 : 2;
+
+ $data = array(
+ "apiKey" => $this->apiKey,
+ "data" => array(
+ "item" => array(
+ array(
+ "text" => $this->getMessage($commit),
+ "type" => $type,
+ ),
+ ),
+ ),
+ );
+
+ $response = $this->send($this->widgetUrl, array('Content-type' => 'application/json'), json_encode($data));
+ return;
+ }
+
+ /**
+ * Send a request
+ *
+ * @param string $url
+ * @param array $headers
+ * @param string $data
+ *
+ * @return
+ */
+ protected function send($url, array $headers = array(), $data)
+ {
+ if ($this->poster !== null) {
+ return call_user_func($this->poster, $url, $headers, $data);
+ }
+
+ /**
+ * See
+ * http://wezfurlong.org/blog/2006/nov/http-post-from-php-without-curl/
+ */
+ $params = array('http' => array(
+ 'method' => 'POST',
+ 'content' => $data
+ ));
+ if (!empty($headers)) {
+ array_walk($headers, function(&$value, $key) {
+ $value = $key . ':' . $value;
+ });
+ $params['http']['header'] = implode("\n", $headers);
+ }
+ $ctx = stream_context_create($params);
+ $fp = @fopen($url, 'rb', false, $ctx);
+ if (!$fp) {
+ return;
+ }
+ $response = @stream_get_contents($fp);
+
+ return;
+ }
+
+
+ /**
+ * Get format
+ *
+ * @return string|callable $format
+ */
+ public function getFormat()
+ {
+ return $this->format;
+ }
+
+ /**
+ * Set format. A custom string (with placeholders, as described in
+ * Sismo\Notifier, or a callback, taking a Commit as it's only parameter
+ *
+ * @param string|callable $format
+ * @return GeckoboardNotifier
+ */
+ public function setFormat($format)
+ {
+ if (!is_string($format) && !is_callable($format)) {
+ throw new \InvalidArgumentException(
+ sprintf("\$format should be string or callable, %s given", gettype($format))
+ );
+ }
+
+ $this->format = $format;
+ return $this;
+ }
+
+ /**
+ * Set poster, only really here for testing so we can avoid an actual HTTP
+ * post
+ *
+ * @param callable $poster
+ * @return GeckoboardNotifier
+ */
+ public function setPoster($poster)
+ {
+ if (!is_callable($poster)) {
+ throw new \InvalidArgumentException(
+ sprintf("\$poster should be callable, %s given", gettype($poster))
+ );
+ }
+
+ $this->poster = $poster;
+ return $this;
+ }
+
+ /**
+ * Get Message
+ *
+ * @param Commit $commit
+ * @return string
+ */
+ protected function getMessage(Commit $commit)
+ {
+ if ($this->format == null) {
+ return $this->getDefaultMessage($commit);
+ }
+
+ if (is_string($this->format)) {
+ return $this->format($this->format, $commit);
+ }
+
+ return call_user_func($this->format, $commit);
+ }
+
+ /**
+ * Get default message
+ *
+ * @param Commit $commit
+ * @return string
+ */
+ protected function getDefaultMessage(Commit $commit)
+ {
+ return $this->format("[%STATUS%]\n%message%\n%author%", $commit);
+ }
+
+}
+
+
216 tests/Davedevelopment/Sismo/GeckoboardNotifierTest.php
@@ -0,0 +1,216 @@
+<?php
+namespace Davedevelopment\Sismo;
+
+/**
+ * Test class for GeckoboardNotifier.
+ * Generated by PHPUnit on 2012-02-25 at 21:59:23.
+ */
+class GeckoboardNotifierTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var GeckoboardNotifier
+ */
+ protected $object;
+
+ /**
+ * Mock data
+ */
+ protected $mockProjectSlug = 'mockProjectSlug';
+ protected $mockProjectName = 'mockProjectName';
+ protected $mockCommitStatus = 'succeeded';
+ protected $mockCommitStatusCode = 'success';
+ protected $mockCommitSha = 'mockCommitSha';
+ protected $mockCommitShortSha = 'mockCommitShortSha';
+ protected $mockCommitAuthor = 'mockCommitAuthor';
+ protected $mockCommitMessage = 'mockCommitMessage';
+
+ /**
+ * Sets up the fixture, for example, opens a network connection.
+ * This method is called before a test is executed.
+ */
+ protected function setUp()
+ {
+ $this->apiKey = "my_api_key";
+ $this->widget = "http://dave.com";
+ $this->object = new GeckoboardNotifier(
+ $this->apiKey,
+ $this->widget
+ );
+
+ $that = $this;
+
+ $this->mockPost = function($url, array $headers = array(), $data) use ($that) {
+ $that->lastUrl = $url;
+ $that->lastData = $data;
+ $that->lastHeaders = $headers;
+ };
+
+ $this->object->setPoster($this->mockPost);
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown()
+ {
+ }
+
+ /**
+ * @covers {className}::{origMethodName}
+ */
+ public function testNotify()
+ {
+ $data = $this->getDataStub();
+ $commit = $this->getCommitMock();
+ $this->object->notify($commit);
+ $this->assertEquals(json_encode($data), $this->lastData);
+ $this->assertEquals($this->widget, $this->lastUrl);
+ }
+
+ /**
+ *
+ */
+ public function testNotifySendsAlertType()
+ {
+ $this->mockCommitStatus = 'failed';
+ $this->mockCommitStatusCode = 'failed';
+ $data = $this->getDataStub();
+ $data['data']['item'][0]['type'] = 1;
+ $commit = $this->getCommitMock();
+ $this->object->notify($commit);
+ $this->assertEquals(json_encode($data), $this->lastData);
+ $this->assertEquals($this->widget, $this->lastUrl);
+ }
+
+ /**
+ *
+ */
+ public function testNotifyUsesCustomStringFormat()
+ {
+ $format = "[%STATUS%]";
+ $data = $this->getDataStub();
+ $data['data']['item'][0]['text'] = "[SUCCEEDED]";
+ $commit = $this->getCommitMock();
+ $this->object->setFormat($format);
+ $this->object->notify($commit);
+ $this->assertEquals(json_encode($data), $this->lastData);
+ $this->assertEquals($this->widget, $this->lastUrl);
+ }
+
+ /**
+ *
+ */
+ public function testNotifyUsesCustomCallableFormat()
+ {
+ $format = function(\Sismo\Commit $commit) {
+ return "dave123";
+ };
+
+ $data = $this->getDataStub();
+ $data['data']['item'][0]['text'] = "dave123";
+ $commit = $this->getCommitMock(false);
+ $this->object->setFormat($format);
+ $this->object->notify($commit);
+ $this->assertEquals(json_encode($data), $this->lastData);
+ $this->assertEquals($this->widget, $this->lastUrl);
+ }
+
+ /**
+ *
+ * Get commit mock
+ *
+ * @param bool $withDefaultExpectations
+ * @return Mock...
+ */
+ protected function getCommitMock($withProject = true, $withDefaultExpectations = true)
+ {
+ $commit = $this->getMock("\\Sismo\\Commit", array(), array(), "", array());
+
+ if ($withProject) {
+ $project = $this->getMock("\\Sismo\\Project", array(), array(), "", array());
+
+ $commit->expects($this->any())
+ ->method("getProject")
+ ->will($this->returnValue($project));
+
+ if ($withDefaultExpectations) {
+ $project->expects($this->atLeastOnce())
+ ->method("getSlug")
+ ->will($this->returnValue($this->mockProjectSlug));
+
+ $project->expects($this->atLeastOnce())
+ ->method("getName")
+ ->will($this->returnValue($this->mockProjectName));
+
+ $commit->expects($this->atLeastOnce())
+ ->method("getStatus")
+ ->will($this->returnValue($this->mockCommitStatus));
+
+ $commit->expects($this->atLeastOnce())
+ ->method("getStatusCode")
+ ->will($this->returnValue($this->mockCommitStatusCode));
+
+ $commit->expects($this->atLeastOnce())
+ ->method("getSha")
+ ->will($this->returnValue($this->mockCommitSha));
+
+ $commit->expects($this->atLeastOnce())
+ ->method("getShortSha")
+ ->will($this->returnValue($this->mockCommitShortSha));
+
+ $commit->expects($this->atLeastOnce())
+ ->method("getAuthor")
+ ->will($this->returnValue($this->mockCommitAuthor));
+
+ $commit->expects($this->atLeastOnce())
+ ->method("getMessage")
+ ->will($this->returnValue($this->mockCommitMessage));
+ }
+ }
+
+ return $commit;
+ }
+
+
+
+ /**
+ * Get url stub
+ *
+ * Saves repeating in tests
+ *
+ */
+ protected function getUrlStub()
+ {
+ return "https://push.geckoboard.com/v1/send/" . $this->widgetKey;
+ }
+
+ /**
+ * Get Data stub
+ *
+ * Saves repeating in tests
+ *
+ */
+ protected function getDataStub()
+ {
+ $data = array(
+ "apiKey" => $this->apiKey,
+ "data" => array(
+ "item" => array(
+ array(
+ "text" => sprintf(
+ "[%s]\n%s\n%s",
+ strtoupper($this->mockCommitStatus),
+ $this->mockCommitMessage,
+ $this->mockCommitAuthor
+ ),
+ "type" => 2,
+ ),
+ ),
+ ),
+ );
+
+ return $data;
+ }
+}
+?>
Please sign in to comment.
Something went wrong with that request. Please try again.