Namespaced models and refactored filters

This commit is contained in:
Graham Campbell
2015-01-02 00:18:19 +00:00
parent 15a6694865
commit 0ccb5e289c
66 changed files with 310 additions and 195 deletions
+1 -2
View File
@@ -157,17 +157,16 @@ return [
'aliases' => [
'API' => 'Dingo\Api\Facade\API',
'App' => 'Illuminate\Support\Facades\App',
'Auth' => 'Illuminate\Support\Facades\Auth',
'Form' => 'Illuminate\Support\Facades\Form',
'HTML' => 'Illuminate\Support\Facades\HTML',
'Input' => 'Illuminate\Support\Facades\Input',
'Redirect' => 'Illuminate\Support\Facades\Redirect',
'Request' => 'Illuminate\Support\Facades\Request',
'Response' => 'Illuminate\Support\Facades\Response',
'Route' => 'Illuminate\Support\Facades\Route',
'Session' => 'Illuminate\Support\Facades\Session',
'Setting' => 'CachetHQ\Cachet\Models\Setting',
'Str' => 'Illuminate\Support\Str',
],
@@ -1,5 +1,6 @@
<?php
use CachetHQ\Cachet\Models\Component;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
@@ -1,5 +1,6 @@
<?php
use CachetHQ\Cachet\Models\Incident;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
@@ -1,5 +1,6 @@
<?php
use CachetHQ\Cachet\Models\Setting;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
+1
View File
@@ -1,5 +1,6 @@
<?php
use CachetHQ\Cachet\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
-66
View File
@@ -1,66 +0,0 @@
<?php
Route::filter('is_setup', 'IsSetupFilter');
Route::filter('has_setting', 'HasSettingFilter');
Route::filter('cors', 'CORSFilter');
Route::filter('allowed_domains', 'AllowedDomainsFilter');
Route::filter('login_throttling', 'LoginThrottlingFilter');
/*
|--------------------------------------------------------------------------
| Authentication Filters
|--------------------------------------------------------------------------
|
| The following filters are used to verify that the user of the current
| session is logged into this application. The "basic" filter easily
| integrates HTTP Basic authentication for quick, simple checking.
|
*/
Route::filter('auth', function () {
if (Auth::guest()) {
if (Request::ajax()) {
return Response::make('Unauthorized', 401);
} else {
return Redirect::guest('auth/login');
}
}
});
Route::filter('auth.basic', function () {
return Auth::basic();
});
/*
|--------------------------------------------------------------------------
| Guest Filter
|--------------------------------------------------------------------------
|
| The "guest" filter is the counterpart of the authentication filters as
| it simply checks that the current user is not logged in. A redirect
| response will be issued if they are, which you may freely change.
|
*/
Route::filter('guest', function () {
if (Auth::check()) {
return Redirect::to('/');
}
});
/*
|--------------------------------------------------------------------------
| CSRF Protection Filter
|--------------------------------------------------------------------------
|
| The CSRF filter is responsible for protecting your application against
| cross-site request forgery attacks. If this special token in a user
| session does not match the one given in this request, we'll bail.
|
*/
Route::filter('csrf', function () {
if (Session::token() !== Input::get('_token')) {
throw new Illuminate\Session\TokenMismatchException();
}
});
-21
View File
@@ -1,21 +0,0 @@
<?php
class AllowedDomainsFilter
{
public function filter($route, $request, $response)
{
// Always allow our own domain.
$ourDomain = Setting::get('app_domain');
$response->headers->set('Access-Control-Allow-Origin', $ourDomain);
// Should we allow anyone else?
if ($setting = Setting::get('allowed_domains')) {
$domains = explode(',', $setting);
foreach ($domains as $domain) {
$response->headers->set('Access-Control-Allow-Origin', $domain);
}
}
return $response;
}
}
-11
View File
@@ -1,11 +0,0 @@
<?php
class CORSFilter
{
public function filter($route, $request, $response)
{
$response->headers->set('Access-Control-Allow-Origin', '*');
return $response;
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
class HasSettingFilter
{
public function filter($route, $request, $settingName)
{
try {
$setting = Setting::where('name', $settingName)->first();
if (!$setting->value) {
return Redirect::to('setup');
}
} catch (Exception $e) {
return Redirect::to('setup');
}
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
class IsSetupFilter
{
public function filter($route, $request)
{
try {
$setting = Setting::where('name', 'app_name')->first();
if ($setting->value) {
return Redirect::to('/dashboard');
}
} catch (Exception $e) {
// do nothing
}
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
use GrahamCampbell\Throttle\Facades\Throttle;
class LoginThrottlingFilter
{
public function filter($route, $request)
{
// check if we've reached the rate limit, but don't hit the throttle yet
// we can hit the throttle later on in the if validation passes
if (!Throttle::check($request, 10, 10)) {
return Redirect::back()
->with('error', 'You have made too many login requests.');
}
}
}
-48
View File
@@ -1,48 +0,0 @@
<?php
if (! function_exists('elixir')) {
/**
* Get the path to a versioned Elixir file.
*
* @param string $file
*
* @return string
*/
function elixir($file)
{
static $manifest = null;
if (is_null($manifest)) {
$manifest = json_decode(file_get_contents(public_path().'/build/rev-manifest.json'), true);
}
if (isset($manifest[$file])) {
return '/build/'.$manifest[$file];
}
throw new InvalidArgumentException("File {$file} not defined in asset manifest.");
}
}
if (! function_exists('set_active')) {
/**
* Set active class if request is in path.
*
* @param string $path
* @param array $classes
* @param string $active
*
* @return string
*/
function set_active($path, $classes = [], $active = 'active')
{
if (Request::is($path)) {
$classes[] = $active;
}
$class = implode(' ', $classes);
return empty($classes) ? '' : "class=\"{$class}\"";
}
}
-88
View File
@@ -1,88 +0,0 @@
<?php
use CachetHQ\Cachet\Transformers\ComponentTransformer;
use Dingo\Api\Transformer\TransformableInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Watson\Validating\ValidatingTrait;
class Component extends Model implements TransformableInterface
{
use SoftDeletingTrait, ValidatingTrait;
/**
* The validation rules.
*
* @var string[]
*/
protected $rules = [
'user_id' => 'integer|required',
'name' => 'required',
'status' => 'integer',
'link' => 'url',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['name', 'description', 'status', 'user_id', 'tags', 'link', 'order'];
/**
* Lookup all of the incidents reported on the component.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function incidents()
{
return $this->hasMany('Incident', 'component_id', 'id');
}
/**
* Finds all components by status.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $status
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeStatus(Builder $query, $status)
{
return $query->where('status', $status);
}
/**
* Finds all components which don't have the given status.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $status
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNotStatus(Builder $query, $status)
{
return $query->where('status', '<>', $status);
}
/**
* Looks up the human readable version of the status.
*
* @return string
*/
public function getHumanStatusAttribute()
{
return trans('cachet.component.status.'.$this->status);
}
/**
* Get the transformer instance.
*
* @return \CachetHQ\Cachet\Transformers\ComponentTransformer
*/
public function getTransformer()
{
return new ComponentTransformer();
}
}
-111
View File
@@ -1,111 +0,0 @@
<?php
use CachetHQ\Cachet\Transformers\IncidentTransformer;
use Dingo\Api\Transformer\TransformableInterface;
use GrahamCampbell\Markdown\Facades\Markdown;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Watson\Validating\ValidatingTrait;
class Incident extends Model implements TransformableInterface
{
use SoftDeletingTrait, ValidatingTrait;
/**
* The validation rules.
*
* @var string[]
*/
protected $rules = [
'user_id' => 'required|integer',
'component_id' => 'integer',
'name' => 'required',
'status' => 'required|integer',
'message' => 'required',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['user_id', 'component_id', 'name', 'status', 'message'];
/**
* The accessors to append to the model's serialized form.
*
* @var string[]
*/
protected $appends = ['humanStatus'];
/**
* An incident belongs to a component.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function component()
{
return $this->belongsTo('Component', 'component_id', 'id');
}
/**
* Returns a human readable version of the status.
*
* @return string
*/
public function getHumanStatusAttribute()
{
$statuses = trans('cachet.incident.status');
return $statuses[$this->status];
}
/**
* Finds the icon to use for each status.
*
* @return string
*/
public function getIconAttribute()
{
switch ($this->status) {
case 1:
return 'ion ion-flag';
case 2:
return 'ion ion-alert';
case 3:
return 'ion ion-eye';
case 4:
return 'ion ion-checkmark';
}
}
/**
* Returns a Markdown formatted version of the status.
*
* @return string
*/
public function getFormattedMessageAttribute()
{
return Markdown::render($this->message);
}
/**
* Get the transformer instance.
*
* @return \CachetHQ\Cachet\Transformers\IncidentTransformer
*/
public function getTransformer()
{
return new IncidentTransformer();
}
/**
* Check if Incident has message.
*
* @return bool
*/
public function hasMessage()
{
return (trim($this->message) !== '');
}
}
-41
View File
@@ -1,41 +0,0 @@
<?php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Str;
use Watson\Validating\ValidatingTrait;
class IncidentTemplate extends Model
{
use ValidatingTrait;
/**
* The validation rules.
*
* @var string[]
*/
protected $rules = [
'name' => 'alpha|required',
'template' => 'required',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['name', 'template'];
/**
* Overrides the models boot method.
*
* @return void
*/
public static function boot()
{
parent::boot();
self::saving(function ($template) {
$template->slug = Str::slug($template->name);
});
}
}
-59
View File
@@ -1,59 +0,0 @@
<?php
use CachetHQ\Cachet\Transformers\MetricTransformer;
use Dingo\Api\Transformer\TransformableInterface;
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
class Metric extends Model implements TransformableInterface
{
use ValidatingTrait;
/**
* The validation rules.
*
* @var string[]
*/
protected $rules = [
'name' => 'required',
'suffix' => 'required',
'display_chart' => 'boolean',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['name', 'suffix', 'description', 'display_chart'];
/**
* Metrics contain many metric points.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function points()
{
return $this->hasMany('MetricPoint', 'metric_id', 'id');
}
/**
* Determines whether a chart should be shown.
*
* @return bool
*/
public function getShouldDisplayAttribute()
{
return $this->display_chart === 1;
}
/**
* Get the transformer instance.
*
* @return \CachetHQ\Cachet\Transformers\MetricTransformer
*/
public function getTransformer()
{
return new MetricTransformer();
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
use Illuminate\Database\Eloquent\Model;
class MetricPoint extends Model
{
/**
* A metric point belongs to a metric unit.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function metric()
{
return $this->belongsTo('Metric', 'id', 'metric_id');
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
class Service extends Model
{
use ValidatingTrait;
/**
* The validation rules.
*
* @var string[]
*/
protected $rules = [
'type' => 'alpha_dash|required',
'active' => 'required|in:0,1',
'properties' => '',
];
/**
* Returns a decoded properties object for the service.
*
* @param string $properties
*
* @return object
*/
public function getPropertiesAttribute($properties)
{
return json_decode($properties);
}
/**
* Sets the properties attribute which auto encodes to a JSON string.
*
* @param mixed $properties
*/
public function setPropertiesAttribute($properties)
{
$this->attributes['properties'] = json_encode($properties);
}
}
-41
View File
@@ -1,41 +0,0 @@
<?php
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['name', 'value'];
/**
* Returns a setting from the database.
*
* @param string $settingName
* @param bool $checkEnv
*
* @return string|null
*/
public static function get($settingName, $checkEnv = true)
{
$setting = null;
try {
$setting = self::whereName($settingName)->first()->value;
} catch (ErrorException $e) {
if ($checkEnv) {
$env = getenv(strtoupper($settingName));
if (!$env) {
return $env;
}
}
return $setting;
}
return $setting;
}
}
-26
View File
@@ -1,26 +0,0 @@
<?php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Watson\Validating\ValidatingTrait;
class Subscriber extends Model
{
use SoftDeletingTrait, ValidatingTrait;
/**
* The validation rules.
*
* @var string[]
*/
protected $rules = [
'email' => 'required|email',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['email'];
}
-62
View File
@@ -1,62 +0,0 @@
<?php
use Illuminate\Auth\Reminders\RemindableInterface;
use Illuminate\Auth\Reminders\RemindableTrait;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\UserTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Hash;
class User extends Model implements UserInterface, RemindableInterface
{
use UserTrait, RemindableTrait;
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'users';
/**
* The hidden properties.
*
* These are excluded when we are serializing the model.
*
* @var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* The properties that cannot be mass assigned.
*
* @var array
*/
protected $guarded = [];
/**
* Hash any password being inserted by default.
*
* @param string $password
*
* @return $this
*/
public function setPasswordAttribute($password)
{
$this->attributes['password'] = Hash::make($password);
return $this;
}
/**
* Returns a Gravatar URL for the users email address.
*
* @param int $size
*
* @return string
*/
public function getGravatarAttribute($size = 200)
{
return sprintf('https://www.gravatar.com/avatar/%s?size=%d', md5($this->email), $size);
}
}
-120
View File
@@ -1,120 +0,0 @@
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class WebHook extends Model
{
const HEAD = 0;
const GET = 1;
const POST = 2;
const PATCH = 3;
const PUT = 4;
const DELETE = 5;
/**
* Returns all responses for a WebHook.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function response()
{
return $this->hasMany('WebHookContent', 'hook_id', 'id');
}
/**
* Returns all active hooks.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeActive(Builder $query)
{
return $query->where('active', 1);
}
/**
* Setups a Ping event that is fired upon a web hook.
*
* @return object
*/
public function ping()
{
return $this->fire('ping', 'Coming live to you from Cachet.');
}
/**
* Fires the actual web hook event.
*
* @param string $eventType the event to send X-Cachet-Event
* @param mixed $data Data to send to the Web Hook
*
* @return object
*/
public function fire($eventType, $data = null)
{
$startTime = microtime(true);
$client = new Client();
$request = $client->createRequest($this->requestMethod, $this->endpoint, [
'headers' => [
'X-Cachet-Event' => $eventType,
],
'body' => $data
]);
try {
$response = $client->send($request);
} catch (ClientException $e) {
// Do nothing with the exception, we want it.
$response = $e->getResponse();
}
$timeTaken = microtime(true) - $startTime;
// Store the request
$hookResponse = new WebHookResponse();
$hookResponse->web_hook_id = $this->id;
$hookResponse->response_code = $response->getStatusCode();
$hookResponse->sent_headers = json_encode($request->getHeaders());
$hookResponse->sent_body = json_encode($data);
$hookResponse->recv_headers = json_encode($response->getHeaders());
$hookResponse->recv_body = json_encode($response->getBody());
$hookResponse->time_taken = $timeTaken;
$hookResponse->save();
return $hookResponse;
}
/**
* Returns a human readable request type name.
*
* @throws \Exception
*
* @return string
*/
public function getRequestMethodAttribute()
{
$requestMethod = null;
switch ($this->request_type) {
case self::HEAD:
return 'HEAD';
case self::GET:
return 'GET';
case self::POST:
return 'POST';
case self::PATCH:
return 'PATCH';
case self::PUT:
return 'PUT';
case self::DELETE:
return 'DELETE';
default:
throw new Exception('Unknown request type value: '.$this->request_type);
}
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
use Illuminate\Database\Eloquent\Model;
class WebHookResponse extends Model
{
/**
* Returns the hook that a response belongs to.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function hook()
{
return $this->belongsTo('WebHook', 'id', 'hook_id');
}
}
-8
View File
@@ -1,8 +0,0 @@
<?php
Route::model('component', 'Component');
Route::model('incident', 'Incident');
Route::model('incident_template', 'IncidentTemplate');
Route::model('setting', 'Setting');
// Route::model('webhook', 'WebHook');
Route::model('user', 'User');
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
Route::api(['after' => 'allowed_domains', 'namespace' => 'CachetHQ\Cachet\Controllers\Api', 'version' => 'v1'], function () {
Route::api(['after' => 'allowed_domains', 'namespace' => 'CachetHQ\Cachet\Http\Controllers\Api', 'version' => 'v1'], function () {
Route::get('components', 'ComponentController@getComponents');
Route::get('components/{id}', 'ComponentController@getComponent');
Route::get('components/{id}/incidents', 'ComponentController@getComponentIncidents');
+3 -3
View File
@@ -1,16 +1,16 @@
<?php
// Prevent access until the app is setup.
Route::group(['before' => 'has_setting:app_name', 'namespace' => 'CachetHQ\Cachet\Controllers'], function () {
Route::group(['before' => 'has_setting:app_name', 'namespace' => 'CachetHQ\Cachet\Http\Controllers'], function () {
Route::get('/', ['as' => 'status-page', 'uses' => 'HomeController@showIndex']);
Route::get('/incident/{incident}', 'HomeController@showIncident');
});
// Setup route.
Route::group(['before' => 'is_setup', 'namespace' => 'CachetHQ\Cachet\Controllers'], function () {
Route::group(['before' => 'is_setup', 'namespace' => 'CachetHQ\Cachet\Http\Controllers'], function () {
Route::controller('/setup', 'SetupController');
});
Route::group(['namespace' => 'CachetHQ\Cachet\Controllers'], function () {
Route::group(['namespace' => 'CachetHQ\Cachet\Http\Controllers'], function () {
Route::get('/rss', 'RssController@feedAction');
});
+2 -2
View File
@@ -1,10 +1,10 @@
<?php
Route::group(['before' => 'has_setting:app_name', 'namespace' => 'CachetHQ\Cachet\Controllers'], function () {
Route::group(['before' => 'has_setting:app_name', 'namespace' => 'CachetHQ\Cachet\Http\Controllers'], function () {
Route::get('/auth/login', ['before' => 'guest', 'as' => 'login', 'uses' => 'AuthController@showLogin']);
Route::post('/auth/login', ['before' => 'guest|csrf|login_throttling', 'as' => 'logout', 'uses' => 'AuthController@postLogin']);
});
Route::group(['before' => 'auth', 'namespace' => 'CachetHQ\Cachet\Controllers'], function () {
Route::group(['before' => 'auth', 'namespace' => 'CachetHQ\Cachet\Http\Controllers'], function () {
Route::get('/auth/logout', ['as' => 'logout', 'uses' => 'AuthController@logoutAction']);
});
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
Route::group(['before' => 'auth', 'prefix' => 'dashboard', 'namespace' => 'CachetHQ\Cachet\Controllers'], function () {
Route::group(['before' => 'auth', 'prefix' => 'dashboard', 'namespace' => 'CachetHQ\Cachet\Http\Controllers'], function () {
// Dashboard
Route::get('/', ['as' => 'dashboard', 'uses' => 'DashboardController@showDashboard']);
+4 -7
View File
@@ -1,5 +1,6 @@
<?php
use Dingo\Api\Facade\API;
use Illuminate\Support\Facades\Log;
/*
@@ -63,15 +64,11 @@ App::down(function () {
/*
|--------------------------------------------------------------------------
| Require The Filters File
| View Composers
|--------------------------------------------------------------------------
|
| Next we will load the filters file for the application. This gives us
| a nice separate location to store our route and application filter
| definitions instead of putting them all in the main routes file.
| Register Cachet's view composers.
|
*/
require app_path().'/filters.php';
require app_path().'/view-composers.php';
require app_path().'/helpers.php';
require app_path('view-composers.php');
@@ -1,35 +0,0 @@
<?php
class ComponentControllerTest extends TestCase
{
public function setUp()
{
$this->repo = Mockery::mock('CachetHQ\Cachet\Repositories\Component\ComponentRepository');
$this->dingo = Mockery::mock('Dingo\Api\Auth\Shield');
}
public function tearDown()
{
Mockery::close();
}
public function test_get_components_method()
{
$this->repo->shouldReceive('all')->once()->andReturn('foo');
$controller = new CachetHQ\Cachet\Controllers\Api\ComponentController($this->repo);
$response = $controller->getComponents();
$this->assertEquals('foo', $response);
}
public function test_get_component_method()
{
$this->repo->shouldReceive('findOrFail')->with(1)->once()->andReturn('foo');
$controller = new CachetHQ\Cachet\Controllers\Api\ComponentController($this->repo);
$response = $controller->getComponent(1);
$this->assertEquals('foo', $response);
}
}
-18
View File
@@ -1,18 +0,0 @@
<?php
class TestCase extends Illuminate\Foundation\Testing\TestCase
{
/**
* Creates the application.
*
* @return \Symfony\Component\HttpKernel\HttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;
$testEnvironment = 'testing';
return require __DIR__.'/../../bootstrap/start.php';
}
}
+2
View File
@@ -1,5 +1,7 @@
<?php
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident;
use Illuminate\Support\Facades\View;
View::composer('index', function ($view) {