Page MenuHomeDevCentral

No OneTemporary

diff --git a/app/Events/JenkinsPayloadEvent.php b/app/Events/JenkinsPayloadEvent.php
new file mode 100644
index 0000000..a2b93e8
--- /dev/null
+++ b/app/Events/JenkinsPayloadEvent.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Nasqueron\Notifications\Events;
+
+use Nasqueron\Notifications\Events\Event;
+use Illuminate\Queue\SerializesModels;
+
+class JenkinsPayloadEvent extends Event {
+ use SerializesModels;
+
+ /**
+ * The gate door which receives the request
+ * @var string
+ */
+ public $door;
+
+ /**
+ * The request content, as a structured data
+ * @var stdClass
+ */
+ public $payload;
+
+ /**
+ * Creates a new event instance.
+ *
+ * @param string $door
+ * @param stdClass $payload
+ */
+ public function __construct($door, $payload) {
+ $this->door = $door;
+ $this->payload = $payload;
+ }
+}
diff --git a/app/Http/Controllers/Gate/JenkinsGateController.php b/app/Http/Controllers/Gate/JenkinsGateController.php
new file mode 100644
index 0000000..d9d6c1f
--- /dev/null
+++ b/app/Http/Controllers/Gate/JenkinsGateController.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Nasqueron\Notifications\Http\Controllers\Gate;
+
+use Event;
+use Request;
+
+use Nasqueron\Notifications\Events\JenkinsPayloadEvent;
+
+class JenkinsGateController extends GateController {
+
+ ///
+ /// Private members
+ ///
+
+ /**
+ * The request content, as a structured data
+ *
+ * @var stdClass
+ */
+ private $payload;
+
+ /**
+ * The request content
+ *
+ * @var string
+ */
+ private $rawRequestContent;
+
+ ///
+ /// Constants
+ ///
+
+ /**
+ * The name of the service this gate accepts payload from.
+ */
+ const SERVICE_NAME = 'Jenkins';
+
+ ///
+ /// Request processing
+ ///
+
+ /**
+ * Handles POST requests
+ *
+ * @param Request $request the HTTP request
+ * @return Illuminate\Http\Response
+ */
+ public function onPost ($door) {
+ // Parses the request and check if it's legit
+
+ $this->door = $door;
+ $this->extractPayload();
+
+ // Process the request
+
+ $this->logRequest();
+ $this->onPayload();
+
+ // Output
+
+ return parent::renderReport();
+ }
+
+ /**
+ * Extracts payload from the request
+ */
+ protected function extractPayload () {
+ $request = Request::instance();
+ $this->rawRequestContent = $request->getContent();
+ $this->payload = json_decode($this->rawRequestContent);
+ }
+
+ ///
+ /// Payload processing
+ ///
+
+ protected function onPayload () {
+ $this->initializeReport();
+
+ Event::fire(new JenkinsPayloadEvent(
+ $this->door,
+ $this->payload
+ ));
+ }
+}
diff --git a/app/Jobs/FireJenkinsNotification.php b/app/Jobs/FireJenkinsNotification.php
new file mode 100644
index 0000000..5f225b1
--- /dev/null
+++ b/app/Jobs/FireJenkinsNotification.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Nasqueron\Notifications\Jobs;
+
+use Nasqueron\Notifications\Notifications\JenkinsNotification;
+use Nasqueron\Notifications\Events\JenkinsPayloadEvent;
+use Nasqueron\Notifications\Events\NotificationEvent;
+use Nasqueron\Notifications\Jobs\Job;
+
+use Event;
+
+class FireJenkinsNotification extends Job {
+
+ /**
+ * @var JenkinsPayloadEvent;
+ */
+ private $event;
+
+ /**
+ * Initializes a new instance of FireJenkinsNotification
+ *
+ * @param JenkinsPayloadEvent $event The event to notify
+ */
+ public function __construct (JenkinsPayloadEvent $event) {
+ $this->event = $event;
+ }
+
+ ///
+ /// Task
+ ///
+
+ /**
+ * Executes the job.
+ *
+ * @return void
+ */
+ public function handle() {
+ $notification = $this->createNotification();
+ Event::fire(new NotificationEvent($notification));
+ }
+
+ /**
+ * Creates a Jenkins notification
+ *
+ * @param JenkinsPayloadEvent $event
+ * @return Notification the notification
+ */
+ protected function createNotification() {
+ return new JenkinsNotification(
+ $this->event->door, // project
+ $this->event->payload // raw content
+ );
+ }
+}
diff --git a/app/Listeners/LastPayloadSaver.php b/app/Listeners/LastPayloadSaver.php
index abb9a56..55e7ce4 100644
--- a/app/Listeners/LastPayloadSaver.php
+++ b/app/Listeners/LastPayloadSaver.php
@@ -1,52 +1,53 @@
<?php
namespace Nasqueron\Notifications\Listeners;
use Nasqueron\Notifications\Events\Event;
class LastPayloadSaver {
///
/// Events handling
///
/**
* Handles payload events
*/
public function onPayload (Event $event) {
self::savePayload($event->payload);
}
/**
* Saves payload to log file
*
* @param string $payload The payload to save
*/
public static function savePayload ($payload) {
$filename = storage_path('logs/payload.json');
$content = json_encode($payload);
file_put_contents($filename, $content);
}
///
/// Events listening
///
/**
* Register the listeners for the subscriber.
*
* @param Illuminate\Events\Dispatcher $events
*/
public function subscribe (\Illuminate\Events\Dispatcher $events) {
$ns = 'Nasqueron\Notifications\Events';
$class = 'Nasqueron\Notifications\Listeners\LastPayloadSaver';
$eventsToListen = [
'DockerHubPayloadEvent',
'GitHubPayloadEvent',
+ 'JenkinsPayloadEvent',
'PhabricatorPayloadEvent',
];
foreach ($eventsToListen as $event) {
$events->listen("$ns\\$event", "$class@onPayload");
}
}
}
diff --git a/app/Listeners/NotificationListener.php b/app/Listeners/NotificationListener.php
index 4b7a99e..ae4c577 100644
--- a/app/Listeners/NotificationListener.php
+++ b/app/Listeners/NotificationListener.php
@@ -1,76 +1,94 @@
<?php
namespace Nasqueron\Notifications\Listeners;
use Nasqueron\Notifications\Events\DockerHubPayloadEvent;
use Nasqueron\Notifications\Events\GitHubPayloadEvent;
+use Nasqueron\Notifications\Events\JenkinsPayloadEvent;
use Nasqueron\Notifications\Events\PhabricatorPayloadEvent;
use Nasqueron\Notifications\Jobs\FireDockerHubNotification;
use Nasqueron\Notifications\Jobs\FireGitHubNotification;
+use Nasqueron\Notifications\Jobs\FireJenkinsNotification;
use Nasqueron\Notifications\Jobs\FirePhabricatorNotification;
class NotificationListener {
///
/// Distill services' payloads into notifications
///
/**
* Handles a Docker Hub payload event.
*
* @param DockerHubPayloadEvent $event
* @return void
*/
public function onDockerHubPayload(DockerHubPayloadEvent $event) {
$job = new FireDockerHubNotification($event);
$job->handle();
}
/**
* Handles a GitHub payload event.
*
* @param GitHubPayloadEvent $event
* @return void
*/
public function onGitHubPayload(GitHubPayloadEvent $event) {
$job = new FireGitHubNotification($event);
$job->handle();
}
/**
* Handles a Phabricator payload event.
*
* @param PhabricatorPayloadEvent $event
* @return void
*/
public function onPhabricatorPayload(PhabricatorPayloadEvent $event) {
$job = new FirePhabricatorNotification($event);
$job->handle();
}
+ /**
+ * Handles a Jenkins payload event.
+ *
+ * @param JenkinsPayloadEvent $event
+ * @return void
+ */
+ public function onJenkinsPayload (JenkinsPayloadEvent $event) {
+ $job = new FireJenkinsNotification($event);
+ $job->handle();
+ }
+
///
/// Events listening
///
/**
* Register the listeners for the subscriber.
*
* @param Illuminate\Events\Dispatcher $events
*/
public function subscribe (\Illuminate\Events\Dispatcher $events) {
$class = 'Nasqueron\Notifications\Listeners\NotificationListener';
$events->listen(
'Nasqueron\Notifications\Events\DockerHubPayloadEvent',
"$class@onDockerHubPayload"
);
$events->listen(
'Nasqueron\Notifications\Events\GitHubPayloadEvent',
"$class@onGitHubPayload"
);
+ $events->listen(
+ 'Nasqueron\Notifications\Events\JenkinsPayloadEvent',
+ "$class@onJenkinsPayload"
+ );
$events->listen(
'Nasqueron\Notifications\Events\PhabricatorPayloadEvent',
"$class@onPhabricatorPayload"
);
+
}
}
diff --git a/app/Notifications/JenkinsNotification.php b/app/Notifications/JenkinsNotification.php
new file mode 100644
index 0000000..7db16a6
--- /dev/null
+++ b/app/Notifications/JenkinsNotification.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Nasqueron\Notifications\Notifications;
+
+use Nasqueron\Notifications\Notification;
+
+/**
+ * A Jenkins notification.
+ *
+ * This handles the JSON payloads sent by the following plugin:
+ * https://wiki.jenkins-ci.org/display/JENKINS/Notification+Plugin
+ */
+class JenkinsNotification extends Notification {
+
+ /**
+ * Initializes a new instance of the JenkinsNotification class.
+ *
+ * @param string $project The project this message is for
+ * @param mixed $payload The message fired by Jenkins notification plugin
+ */
+ public function __construct ($project, $payload) {
+ // Straightforward properties
+ $this->service = "Jenkins";
+ $this->project = $project;
+ $this->rawContent = $payload;
+
+ // Properties from the payload
+ $this->group = $this->getGroup();
+ $this->text = $this->getText();
+ $this->link = $payload->build->full_url;
+ $this->type = $this->getType();
+ }
+
+ /**
+ * Gets the notification type.
+ *
+ * @return string
+ */
+ public function getType () {
+ $build = $this->rawContent->build;
+
+ $type = strtolower($build->phase);
+
+ if (property_exists($build, 'status')) {
+ $type .= '.';
+ $type .= $build->status;
+ }
+
+ return strtolower($type);
+ }
+
+ /**
+ * Gets the notification text. Intended to convey a short message (thing Twitter or IRC).
+ *
+ * @return string
+ */
+ public function getText () {
+ $name = $this->rawContent->name;
+
+ $build = $this->rawContent->build;
+ $phase = strtolower($build->phase);
+
+ $text = "Jenkins job $name has been $phase";
+
+ if (property_exists($build, 'status')) {
+ $status = strtolower($build->status);
+ $text .= ": $status";
+ }
+
+ return $text;
+ }
+
+ /**
+ * Gets the notification group.
+ *
+ * @return string
+ */
+ public function getGroup () {
+ return "ci"; // This is a temporary group, intended before mapping.
+ }
+
+}
diff --git a/config/gate.php b/config/gate.php
index de9e5bc..a8955a9 100644
--- a/config/gate.php
+++ b/config/gate.php
@@ -1,21 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Gate controllers
|--------------------------------------------------------------------------
|
| Notifications center accept payload from several services and calls
| matching gate controllers to process messages.
|
*/
'controllers' => [
'DockerHub',
'GitHub',
+ 'Jenkins',
'Phabricator',
],
];
diff --git a/tests/Http/PayloadFullTest.php b/tests/Http/PayloadFullTest.php
index d25c244..fd654a6 100644
--- a/tests/Http/PayloadFullTest.php
+++ b/tests/Http/PayloadFullTest.php
@@ -1,153 +1,172 @@
<?php
namespace Nasqueron\Notifications\Tests;
use Keruald\Broker\BlackholeBroker;
use Nasqueron\Notifications\Features;
class PayloadFullTest extends TestCase {
public function setUp () {
parent::setUp();
$this->disableBroker();
}
/**
* Sends a GitHub ping payload to the application, with a valid signature
*/
protected function sendValidTestPayload () {
return $this->sendTestPayload('sha1=25f6cbd17ea4c6c69958b95fb88c879de4b66dcc');
}
/**
* Sends a GitHub ping payload to the application, with a valid signature
*/
protected function sendInvalidTestPayload () {
return $this->sendTestPayload('sha1=somethingwrong');
}
protected function sendTestPayload ($signature) {
$payload = file_get_contents(__DIR__ . '/../data/payloads/GitHubPingPayload.json');
$this->sendPayload(
'/gate/GitHub/Acme', // A gate existing in data/credentials.json
$payload,
'POST',
[
'X-Github-Event' => 'ping',
'X-Github-Delivery' => 'e5dd9fc7-17ac-11e5-9427-73dad6b9b17c',
'X-Hub-Signature' => $signature,
]
);
return $this;
}
/**
* Tests a GitHub gate payload.
*/
public function testPost () {
$this->sendValidTestPayload()->seeJson([
'gate' => 'GitHub',
'door' => 'Acme',
'action' => 'AMQPAction'
]);
$this->assertResponseOk();
}
/**
* Tests a DockerHub gate payload.
*/
public function testDockerHubPayload () {
$payload = file_get_contents(__DIR__ . '/../data/payloads/DockerHubPushPayload.json');
$this->sendPayload(
'/gate/DockerHub/Acme', // A gate existing in data/credentials.json
$payload,
'POST',
[]
)->seeJson([
'gate' => 'DockerHub',
'door' => 'Acme',
'action' => 'AMQPAction'
]);
$this->assertResponseOk();
}
+ /**
+ * Tests a Jenkins gate payload.
+ */
+ public function testJenkinsPayload () {
+ $payload = file_get_contents(__DIR__ . '/../data/payloads/JenkinsPayload.json');
+
+ $this->sendPayload(
+ '/gate/Jenkins/Acme', // A gate existing in data/credentials.json
+ $payload,
+ 'POST',
+ []
+ )->seeJson([
+ 'gate' => 'Jenkins',
+ 'door' => 'Acme',
+ 'action' => 'AMQPAction'
+ ]);
+ $this->assertResponseOk();
+ }
+
/**
* Tests a Phabricator gate payload.
*/
public function testPhabricatorPayload () {
$data = [
'storyID' => 3849,
'storyType' => 'PhabricatorApplicationTransactionFeedStory',
'storyData[objectPHID]' => 'PHID-TASK-l34fw5wievp6n6rnvpuk',
'storyData[transactionPHIDs][PHID-XACT-TASK-by2g3dtlfq3l2wc]' => 'PHID-XACT-TASK-by2g3dtlfq3l2wc',
'storyAuthorPHID' => 'PHID-USER-fnetlprx7zdotfm2hdrz',
'storyText' => 'quux moved T123: Lorem ipsum dolor to Backlog on the Foo workboard.',
'epoch' => 1450654419,
];
$this->post('/gate/Phabricator/Acme', $data)->seeJson([
'gate' => 'Phabricator',
'door' => 'Acme',
'action' => 'AMQPAction'
]);
$this->assertResponseOk();
$this->post('/gate/Phabricator/NotExistingDoor', $data);
$this->assertResponseStatus(404);
}
/**
* Same than testPost, but without actions report.
*/
public function testPostWithoutActionsReport () {
Features::disable("ActionsReport");
$this->sendValidTestPayload();
$this->assertEmpty($this->response->getContent());
$this->assertResponseOk();
// Let's throw an Exception at broker level.
// Without ActionsReport, the client must always receive a 200 OK.
$this->app->instance('broker', function ($app) {
// A non omnipotent instance, so it doesn't mock connect().
return new BlackholeBroker;
});
$this->sendValidTestPayload();
$this->assertEmpty($this->response->getContent());
$this->assertResponseOk();
}
/**
* Tests a GitHub gate payload.
*/
public function testInvalidSignature () {
$this->sendInvalidTestPayload()
->assertResponseStatus(403);
}
public function testBrokerIssue () {
$this->mockNotOperationalBroker();
$payload = file_get_contents(__DIR__ . '/../data/payloads/GitHubPingPayload.json');
$this->sendPayload(
'/gate/GitHub/Acme', // A gate existing in data/credentials.json
$payload,
'POST',
[
'X-Github-Event' => 'ping',
'X-Github-Delivery' => 'e5dd9fc7-17ac-11e5-9427-73dad6b9b17c',
'X-Hub-Signature' => 'sha1=25f6cbd17ea4c6c69958b95fb88c879de4b66dcc',
]
)->seeJson([
'gate' => 'GitHub',
'door' => 'Acme',
'action' => 'AMQPAction',
'type' => 'RuntimeException',
]);
$this->assertResponseStatus(503);
}
}
diff --git a/tests/Notifications/JenkinsNotificationTest.php b/tests/Notifications/JenkinsNotificationTest.php
new file mode 100644
index 0000000..9333c5e
--- /dev/null
+++ b/tests/Notifications/JenkinsNotificationTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Nasqueron\Notifications\Tests\Notifications;
+
+use Nasqueron\Notifications\Notifications\JenkinsNotification;
+use Nasqueron\Notifications\Tests\TestCase;
+
+class JenkinsNotificationTest extends TestCase {
+ /**
+ * @var Nasqueron\Notifications\Notifications\JenkinsNotification
+ */
+ private $notification;
+
+ /**
+ * @var stdClass
+ */
+ private $payload;
+
+ public function prepareNotification ($payloadFile) {
+ $path = __DIR__ . '/../data/payloads/' . $payloadFile;
+ $this->payload = json_decode(file_get_contents($path));
+
+ $this->notification = new JenkinsNotification(
+ "Acme",
+ $this->payload
+ );
+ }
+
+ public function testProperties () {
+ $this->prepareNotification('JenkinsPayload.json');
+
+ $this->assertSame("Jenkins", $this->notification->service);
+ $this->assertSame("Acme", $this->notification->project);
+ $this->assertSame("ci", $this->notification->group);
+ $this->assertSame($this->payload, $this->notification->rawContent);
+ $this->assertSame("completed.success", $this->notification->type);
+ $this->assertSame(
+ "Jenkins job asgard has been completed: success",
+ $this->notification->text
+ );
+ $this->assertSame(
+ "http://localhost:8080/job/asgard/18/",
+ $this->notification->link
+ );
+ }
+
+ public function testPropertiesForIncompletePayload () {
+ $this->prepareNotification('JenkinsStartedPayload.json');
+
+ $this->assertSame("started", $this->notification->type);
+ $this->assertSame(
+ "Jenkins job asgard has been started",
+ $this->notification->text
+ );
+ }
+}
diff --git a/tests/data/payloads/JenkinsPayload.json b/tests/data/payloads/JenkinsPayload.json
new file mode 100644
index 0000000..6afcea3
--- /dev/null
+++ b/tests/data/payloads/JenkinsPayload.json
@@ -0,0 +1,25 @@
+{
+ "name": "asgard",
+ "url": "job/asgard/",
+ "build": {
+ "full_url": "http://localhost:8080/job/asgard/18/",
+ "number": 18,
+ "phase": "COMPLETED",
+ "status": "SUCCESS",
+ "url": "job/asgard/18/",
+ "scm": {
+ "url": "https://github.com/evgeny-goldin/asgard.git",
+ "branch": "origin/master",
+ "commit": "c6d86dc654b12425e706bcf951adfe5a8627a517"
+ },
+ "artifacts": {
+ "asgard.war": {
+ "archive": "http://localhost:8080/job/asgard/18/artifact/asgard.war"
+ },
+ "asgard-standalone.jar": {
+ "archive": "http://localhost:8080/job/asgard/18/artifact/asgard-standalone.jar",
+ "s3": "https://s3-eu-west-1.amazonaws.com/evgenyg-bakery/asgard/asgard-standalone.jar"
+ }
+ }
+ }
+}
diff --git a/tests/data/payloads/JenkinsStartedPayload.json b/tests/data/payloads/JenkinsStartedPayload.json
new file mode 100644
index 0000000..ad5c521
--- /dev/null
+++ b/tests/data/payloads/JenkinsStartedPayload.json
@@ -0,0 +1,15 @@
+{
+ "name": "asgard",
+ "url": "job/asgard/",
+ "build": {
+ "full_url": "http://localhost:8080/job/asgard/18/",
+ "number": 18,
+ "phase": "STARTED",
+ "url": "job/asgard/18/",
+ "scm": {
+ "url": "https://github.com/evgeny-goldin/asgard.git",
+ "branch": "origin/master",
+ "commit": "c6d86dc654b12425e706bcf951adfe5a8627a517"
+ }
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 18:43 (3 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2258682
Default Alt Text
(20 KB)

Event Timeline