Page MenuHomeDevCentral

No OneTemporary

diff --git a/app/Features.php b/app/Config/Features.php
similarity index 98%
rename from app/Features.php
rename to app/Config/Features.php
index c6d2c53..c9d13c3 100644
--- a/app/Features.php
+++ b/app/Config/Features.php
@@ -1,91 +1,91 @@
<?php
-namespace Nasqueron\Notifications;
+namespace Nasqueron\Notifications\Config;
use Config;
/**
* The features class offers a sugar syntax to check if a feature is enabled
* in the Config repository, at app.features.
*
* Features could be added to config/app.php to the features array.
*/
class Features {
///
/// Feature information
///
/**
* @param string $feature The feature to get the config key
* @return string The config key
*/
private static function getFeatureConfigKey (string $feature) : string {
return 'app.features.' . $feature;
}
/**
* Determines if the specified feature is enabled.
*
* @param string $feature The feature to check in the config
* @return bool
*/
public static function isEnabled (string $feature) : bool {
$key = self::getFeatureConfigKey($feature);
return Config::has($key) && (bool)Config::get($key);
}
/**
* Enables a feature in our current configuration instance.
*
* @param string $feature The feature
*/
public static function enable (string $feature) : void {
$key = self::getFeatureConfigKey($feature);
Config::set($key, true);
}
/**
* Disables a feature in our current configuration instance.
*
* @param string $feature The feature
*/
public static function disable (string $feature) : void {
$key = self::getFeatureConfigKey($feature);
Config::set($key, false);
}
///
/// Features lists
///
/**
* Gets all the features, with the toggle status.
*
* @return array An array with features as keys, bool as values (true if enabled)
*/
public static function getAll () : array {
return Config::get('app.features');
}
/**
* Lists all the features.
*
* @return string[] a list of all features
*/
public static function getAvailable () : array {
$features = self::getAll();
return array_keys($features);
}
/**
* Lists the enabled features
*
* @return string[] a list of enabled features
*/
public static function getEnabled () : array {
$features = self::getAll();
$enabledFeatures = array_filter($features);
return array_keys($enabledFeatures);
}
}
diff --git a/app/Console/Commands/ConfigShow.php b/app/Console/Commands/ConfigShow.php
index 86f11ba..626898c 100644
--- a/app/Console/Commands/ConfigShow.php
+++ b/app/Console/Commands/ConfigShow.php
@@ -1,131 +1,131 @@
<?php
namespace Nasqueron\Notifications\Console\Commands;
use Illuminate\Console\Command;
-use Nasqueron\Notifications\Features;
+use Nasqueron\Notifications\Config\Features;
use Nasqueron\Notifications\Services\Service;
use Config;
use ProjectsMap;
use Services;
class ConfigShow extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'config:show';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Show notifications center configuration';
/**
* Creates a new command instance.
*/
public function __construct () {
parent::__construct();
}
///
/// Prepare information tables
///
/**
* Gets the services (defined in credentials.json) as table rows.
*
* @return \Nasqueron\Notifications\Services\Service[]
*/
protected function getServicesTableRows () : array {
$rows = [];
foreach (Services::get() as $service) {
$rows[] = [
$service->gate,
$service->door,
$service->getInstanceName(),
$this->getServiveStatus($service)
];
}
return $rows;
}
/**
* Gets service status.
*
* @param \Nasqueron\Notifications\Services\Service $service The service to check
* @return string A description of the issue if something is wrong; otherwise, "✓".
*/
protected function getServiveStatus (Service $service) : string {
if ($service->gate === 'Phabricator') {
// Ensure the projects map is cached
$map = \ProjectsMap::fetch($service->door);
if (!$map->isCached()) {
return "Projects map not cached.";
}
}
return "✓";
}
/**
* Gets features as table rows
*
* @return array
*/
protected function getFeaturesTableRows () : array {
$rows = [];
foreach (Features::getAll() as $key => $value) {
if ($value) {
$checkMark = '✓';
} else {
$checkMark = '';
}
$rows[] = [$key, $checkMark];
}
return $rows;
}
///
/// Handle the command
///
/**
* Executes the console command.
*/
public function handle () : void {
$this->printGates();
$this->printFeatures();
$this->printServices();
}
protected final function printGates () : void {
$this->info("Gates:\n");
foreach (Config::get('gate.controllers') as $gate) {
$this->line('- ' . $gate);
}
}
protected final function printFeatures () : void {
$this->info("\nFeatures:\n");
$this->table(
['Feature', 'Enabled'],
$this->getFeaturesTableRows()
);
}
protected final function printServices () : void {
$this->info("\nServices declared in credentials:\n");
$this->table(
['Gate', 'Door', 'Instance', 'Status'],
$this->getServicesTableRows()
);
}
}
diff --git a/app/Http/Controllers/Gate/GateController.php b/app/Http/Controllers/Gate/GateController.php
index adaf680..ab8aae7 100644
--- a/app/Http/Controllers/Gate/GateController.php
+++ b/app/Http/Controllers/Gate/GateController.php
@@ -1,120 +1,120 @@
<?php
namespace Nasqueron\Notifications\Http\Controllers\Gate;
-use Nasqueron\Notifications\Features;
+use Nasqueron\Notifications\Config\Features;
use Nasqueron\Notifications\Http\Controllers\Controller;
use Nasqueron\Notifications\Services\Service;
use Symfony\Component\HttpFoundation\Response as BaseResponse;
use Illuminate\View\View;
use App;
use Log;
use Report;
use Response;
use Services;
/**
* Represents a controller handling an entry-point for API payloads
*/
class GateController extends Controller {
///
/// Private members
///
/**
* @var string
*/
protected $door;
///
/// Requests
///
/**
* Handles GET requests
*/
public function onGet () : View {
// Virtually all the push APIs will send they payloads
// using a POST request, so we can provide a sensible
// default GET error message.
return view('gate/ispostonly');
}
/**
* Logs the request
*/
protected function logRequest (array $extraContextualData = []) : void {
Log::info('[Gate] New payload.', [
'service' => static::SERVICE_NAME,
'door' => $this->door,
] + $extraContextualData);
}
///
/// Reports
///
/**
* Initializes the report and registers it
*/
protected function initializeReport () : void {
if (Features::isEnabled('ActionsReport')) {
Report::attachToGate(static::SERVICE_NAME, $this->door);
}
}
/**
* Renders the report
*
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function renderReport () : BaseResponse {
if (!Features::isEnabled('ActionsReport')) {
return response("");
}
$report = App::make('report');
$statusCode = $report->containsError() ? 503 : 200;
return Response::json($report)
->setStatusCode($statusCode);
}
///
/// Credentials
///
/**
* Gets service credentials for this gate and door
*
* @return Nasqueron\Notifications\Services\Service|null The service information is found; otherwise, null.
*/
public function getService () : ?Service {
return Services::findServiceByDoor(static::SERVICE_NAME, $this->door);
}
/**
* Checks if a registered service exists for this service and door.
*/
protected function doesServiceExist () : bool {
return $this->getService() !== null;
}
/**
* Gets secret for this service and door.
*
* @return string the secret, or if unknown, an empty string
*/
protected function getSecret () : string {
$service= $this->getService();
if ($service !== null) {
return $service->secret;
}
return "";
}
}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index e5118bb..98d64d9 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -1,32 +1,32 @@
<?php
-use Nasqueron\Notifications\Features;
+use Nasqueron\Notifications\Config\Features;
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
Route::get('/', function () {
return view('welcome');
});
// Allows to external tool to ping your instalation and know if the site is up.
Route::get('/status', function() {
return "ALIVE";
});
// Gate controllers
if (Features::isEnabled('Gate')) {
foreach (Config::get('gate.controllers') as $controller) {
$controllerRoute = '/gate/' . $controller . '/';
Route::get($controllerRoute . '{door?}', "Gate\\${controller}GateController@onGet");
Route::post($controllerRoute . '{door}', "Gate\\${controller}GateController@onPost");
}
}
diff --git a/tests/Console/Commands/ConfigShowTest.php b/tests/Console/Commands/ConfigShowTest.php
index 3209042..59507dc 100644
--- a/tests/Console/Commands/ConfigShowTest.php
+++ b/tests/Console/Commands/ConfigShowTest.php
@@ -1,97 +1,97 @@
<?php
namespace Nasqueron\Notifications\Tests\Console\Commands;
-use Nasqueron\Notifications\Features;
+use Nasqueron\Notifications\Config\Features;
use Nasqueron\Notifications\Services\Service;
use Mockery;
class ConfigShowTest extends TestCase {
/**
* @var string
*/
protected $class = 'Nasqueron\Notifications\Console\Commands\ConfigShow';
/**
* Nasqueron\Notifications\Services\Services
*/
private $servicesMock;
public function setUp () {
parent::setUp();
$this->servicesMock = $this->mockServices();
}
public function testRegularExecute () {
//Our command calls Services::get()
$this->servicesMock->shouldReceive('get')->once()->andReturn([]);
$this->tester->execute(['command' => $this->command->getName()]);
$this->assertRegexp('/Gates/', $this->tester->getDisplay());
$this->assertRegexp('/Features/', $this->tester->getDisplay());
$this->assertRegexp('/Services declared/', $this->tester->getDisplay());
}
public function testRegularExecuteWithService () {
$service = $this->mockService();
$this->servicesMock
->shouldReceive('get')
->once()
->andReturn([$service]);
$this->tester->execute(['command' => $this->command->getName()]);
$this->assertRegexp('/Storm/', $this->tester->getDisplay());
}
public function testRegularExecuteWithPhabricatorService () {
$this->mockPhabricatorAPIForProjectsMap();
$service = $this->mockService('Phabricator');
$this->servicesMock
->shouldReceive('get')
->once()
->andReturn([$service]);
$this->servicesMock
->shouldReceive('findServiceByProperty');
$this->tester->execute(['command' => $this->command->getName()]);
$this->assertRegexp('/Phabricator.*Projects map not cached./', $this->tester->getDisplay());
}
protected function mockProjectsMap () {
$mock = Mockery::mock('Nasqueron\Notifications\Phabricator\ProjectsMap');
$this->app->instance('phabricator-projectsmap', $mock);
return $mock;
}
public function testRegularExecuteWithPhabricatorServiceWhenTheProjectsMapIsCached () {
// The services list will return only one, for the Phabricator gate.
$service = $this->mockService('Phabricator');
$this->servicesMock
->shouldReceive('get')->once()->andReturn([$service]);
// The project map (built by the factory) will say it's cached.
$this->mockProjectsMap()
->shouldReceive('fetch->isCached')->once()->andReturn(true);
$this->tester->execute(['command' => $this->command->getName()]);
$this->assertRegexp('/Phabricator.*✓/', $this->tester->getDisplay());
}
public function testExecuteWhenSomeFeatureIsDisabled () {
Features::disable('ActionsReport');
$this->servicesMock->shouldReceive('get')->once()->andReturn([]);
$this->tester->execute(['command' => $this->command->getName()]);
$this->assertRegexp('/Gate *\| *✓ *\|/', $this->tester->getDisplay());
$this->assertRegexp('/ActionsReport *\| *\|/', $this->tester->getDisplay());
}
}
diff --git a/tests/FeaturesTest.php b/tests/FeaturesTest.php
index 500f5af..cbf85f2 100644
--- a/tests/FeaturesTest.php
+++ b/tests/FeaturesTest.php
@@ -1,28 +1,28 @@
<?php
namespace Nasqueron\Notifications\Tests;
-use Nasqueron\Notifications\Features;
+use Nasqueron\Notifications\Config\Features;
class FeaturesTest extends TestCase {
public function testEnable () {
// Find it (en vain …)
$this->assertNotContains('Quux', Features::getEnabled());
$this->assertFalse(Features::isEnabled('Quux'));
// Enable it
Features::enable('Quux');
$this->assertTrue(Features::isEnabled('Quux'));
$this->assertContains('Quux', Features::getEnabled());
// Disable it
Features::disable('Quux');
$this->assertFalse(Features::isEnabled('Quux'));
// Count it
$this->assertContains('Quux', Features::getAll());
$this->assertContains('Quux', Features::getAvailable());
$this->assertNotContains('Quux', Features::getEnabled());
}
}
diff --git a/tests/Http/PayloadFullTest.php b/tests/Http/PayloadFullTest.php
index 86973e6..0461da3 100644
--- a/tests/Http/PayloadFullTest.php
+++ b/tests/Http/PayloadFullTest.php
@@ -1,183 +1,183 @@
<?php
namespace Nasqueron\Notifications\Tests;
use Keruald\Broker\BlackholeBroker;
-use Nasqueron\Notifications\Features;
+use Nasqueron\Notifications\Config\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();
}
private function getDataForPhabricatorPayloadTests () {
return [
'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,
];
}
/**
* Tests a Phabricator gate payload.
*/
public function testPhabricatorPayload () {
$data = $this->getDataForPhabricatorPayloadTests();
$this->post('/gate/Phabricator/Acme', $data)->seeJson([
'gate' => 'Phabricator',
'door' => 'Acme',
'action' => 'AMQPAction'
]);
$this->assertResponseOk();
}
/**
* Tests a Phabricator gate payload, when the door doesn't exist.
*/
public function testPhabricatorPayloadOnNotExistingDoor () {
$data = $this->getDataForPhabricatorPayloadTests();
$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);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Oct 11, 22:14 (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3064159
Default Alt Text
(20 KB)

Event Timeline