Merge pull request #2570 from CachetHQ/system-meta

Global Meta System
This commit is contained in:
James Brooks
2017-06-13 22:47:23 +01:00
committed by GitHub
15 changed files with 290 additions and 14 deletions

View File

@@ -96,6 +96,13 @@ final class CreateIncidentCommand
*/ */
public $template_vars; public $template_vars;
/**
* Meta key/value pairs.
*
* @var array
*/
public $meta = [];
/** /**
* The validation rules. * The validation rules.
* *
@@ -112,6 +119,7 @@ final class CreateIncidentCommand
'stickied' => 'required|bool', 'stickied' => 'required|bool',
'occurred_at' => 'nullable|string', 'occurred_at' => 'nullable|string',
'template' => 'nullable|string', 'template' => 'nullable|string',
'meta' => 'required|array',
]; ];
/** /**
@@ -128,10 +136,11 @@ final class CreateIncidentCommand
* @param string|null $occurred_at * @param string|null $occurred_at
* @param string|null $template * @param string|null $template
* @param array $template_vars * @param array $template_vars
* @param array $meta
* *
* @return void * @return void
*/ */
public function __construct($name, $status, $message, $visible, $component_id, $component_status, $notify, $stickied, $occurred_at, $template, array $template_vars = []) public function __construct($name, $status, $message, $visible, $component_id, $component_status, $notify, $stickied, $occurred_at, $template, array $template_vars = [], $meta = [])
{ {
$this->name = $name; $this->name = $name;
$this->status = $status; $this->status = $status;
@@ -144,5 +153,6 @@ final class CreateIncidentCommand
$this->occurred_at = $occurred_at; $this->occurred_at = $occurred_at;
$this->template = $template; $this->template = $template;
$this->template_vars = $template_vars; $this->template_vars = $template_vars;
$this->meta = $meta;
} }
} }

View File

@@ -18,6 +18,7 @@ use CachetHQ\Cachet\Bus\Exceptions\Incident\InvalidIncidentTimestampException;
use CachetHQ\Cachet\Models\Component; use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident; use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate; use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\Meta;
use CachetHQ\Cachet\Services\Dates\DateFactory; use CachetHQ\Cachet\Services\Dates\DateFactory;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
@@ -100,6 +101,18 @@ class CreateIncidentCommandHandler
// Create the incident // Create the incident
$incident = Incident::create($data); $incident = Incident::create($data);
// Store any meta?
if ($meta = $command->meta) {
foreach ($meta as $key => $value) {
Meta::create([
'key' => $key,
'value' => $value,
'meta_type' => 'incidents',
'meta_id' => $incident->id,
]);
}
}
// Update the component. // Update the component.
if ($component = Component::find($command->component_id)) { if ($component = Component::find($command->component_id)) {
dispatch(new UpdateComponentCommand( dispatch(new UpdateComponentCommand(

View File

@@ -14,6 +14,7 @@ namespace CachetHQ\Cachet\Foundation\Providers;
use AltThree\Bus\Dispatcher; use AltThree\Bus\Dispatcher;
use CachetHQ\Cachet\Bus\Middleware\UseDatabaseTransactions; use CachetHQ\Cachet\Bus\Middleware\UseDatabaseTransactions;
use CachetHQ\Cachet\Services\Dates\DateFactory; use CachetHQ\Cachet\Services\Dates\DateFactory;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@@ -42,6 +43,14 @@ class AppServiceProvider extends ServiceProvider
Str::macro('canonicalize', function ($url) { Str::macro('canonicalize', function ($url) {
return preg_replace('/([^\/])$/', '$1/', $url); return preg_replace('/([^\/])$/', '$1/', $url);
}); });
Relation::morphMap([
'components' => \CachetHQ\Cachet\Models\Component::class,
'incidents' => \CachetHQ\Cachet\Models\Incident::class,
'metrics' => \CachetHQ\Cachet\Models\Metric::class,
'schedules' => \CachetHQ\Cachet\Models\Schedule::class,
'subscriber' => \CachetHQ\Cachet\Models\Subscriber::class,
]);
} }
/** /**

View File

@@ -78,7 +78,8 @@ class IncidentController extends AbstractApiController
Binput::get('stickied', false), Binput::get('stickied', false),
Binput::get('occurred_at'), Binput::get('occurred_at'),
Binput::get('template'), Binput::get('template'),
Binput::get('vars', []) Binput::get('vars', []),
Binput::get('meta', [])
)); ));
} catch (QueryException $e) { } catch (QueryException $e) {
throw new BadRequestHttpException(); throw new BadRequestHttpException();

View File

@@ -134,6 +134,16 @@ class Component extends Model implements HasPresenter
return $this->hasMany(Incident::class, 'component_id', 'id'); return $this->hasMany(Incident::class, 'component_id', 'id');
} }
/**
* Get all of the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/** /**
* Get the tags relation. * Get the tags relation.
* *

View File

@@ -145,7 +145,10 @@ class Incident extends Model implements HasPresenter
* *
* @var string[] * @var string[]
*/ */
protected $with = ['updates']; protected $with = [
'meta',
'updates',
];
/** /**
* Get the component relation. * Get the component relation.
@@ -157,6 +160,16 @@ class Incident extends Model implements HasPresenter
return $this->belongsTo(Component::class, 'component_id', 'id'); return $this->belongsTo(Component::class, 'component_id', 'id');
} }
/**
* Get all of the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/** /**
* Get the updates relation. * Get the updates relation.
* *

80
app/Models/Meta.php Normal file
View File

@@ -0,0 +1,80 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use Illuminate\Database\Eloquent\Model;
/**
* This is the meta model class.
*
* @author James Brooks <james@alt-three.com>
*/
class Meta extends Model
{
use ValidatingTrait;
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'id' => 'int',
'key' => 'string',
'value' => 'json',
'meta_id' => 'int',
'meta_type' => 'string',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = [
'key',
'value',
'meta_id',
'meta_type',
];
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'id' => 'nullable|int|min:1',
'key' => 'required|string',
'value' => 'nullable',
'meta_id' => 'required|int',
'meta_type' => 'required|string',
];
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'meta';
/**
* Get all of the owning meta models.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function meta()
{
return $this->morphTo();
}
}

View File

@@ -165,6 +165,16 @@ class Metric extends Model implements HasPresenter
}); });
} }
/**
* Get all of the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/** /**
* Get the points relation. * Get the points relation.
* *

View File

@@ -131,6 +131,26 @@ class Schedule extends Model implements HasPresenter
*/ */
protected $with = ['components']; protected $with = ['components'];
/**
* Get the components relation.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function components()
{
return $this->hasMany(ScheduleComponent::class);
}
/**
* Get all of the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/** /**
* Scopes schedules to those in the future. * Scopes schedules to those in the future.
* *
@@ -155,16 +175,6 @@ class Schedule extends Model implements HasPresenter
return $query->where('status', '<', self::COMPLETE)->where('scheduled_at', '<=', Carbon::now()); return $query->where('status', '<', self::COMPLETE)->where('scheduled_at', '<=', Carbon::now());
} }
/**
* Get the components relation.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function components()
{
return $this->hasMany(ScheduleComponent::class);
}
/** /**
* Get the presenter class. * Get the presenter class.
* *

View File

@@ -90,6 +90,16 @@ class Subscriber extends Model implements HasPresenter
}); });
} }
/**
* Get all of the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/** /**
* Get the subscriptions relation. * Get the subscriptions relation.
* *

View File

@@ -279,6 +279,16 @@ class IncidentPresenter extends BasePresenter implements Arrayable
return 0; return 0;
} }
/**
* Return the meta in a key value pair.
*
* @return array
*/
public function meta()
{
return $this->wrappedObject->meta->pluck('value', 'key')->all();
}
/** /**
* Convert the presenter instance to an array. * Convert the presenter instance to an array.
* *
@@ -294,6 +304,7 @@ class IncidentPresenter extends BasePresenter implements Arrayable
'latest_icon' => $this->latest_icon(), 'latest_icon' => $this->latest_icon(),
'permalink' => $this->permalink(), 'permalink' => $this->permalink(),
'duration' => $this->duration(), 'duration' => $this->duration(),
'meta' => $this->meta(),
'occurred_at' => $this->occurred_at(), 'occurred_at' => $this->occurred_at(),
'created_at' => $this->created_at(), 'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(), 'updated_at' => $this->updated_at(),

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMetaTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('meta', function (Blueprint $table) {
$table->increments('id');
$table->string('key')->index();
$table->string('value');
$table->integer('meta_id')->unsigned();
$table->string('meta_type');
$table->timestamps();
$table->index(['meta_id', 'meta_type']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('meta');
}
}

View File

@@ -154,4 +154,24 @@ class IncidentTest extends AbstractApiTestCase
$this->delete('/api/v1/incidents/1'); $this->delete('/api/v1/incidents/1');
$this->assertResponseStatus(204); $this->assertResponseStatus(204);
} }
public function testCreateIncidentWithMeta()
{
$this->beUser();
$this->post('/api/v1/incidents', [
'name' => 'Foo',
'message' => 'Lorem ipsum dolor sit amet',
'status' => 1,
'meta' => [
'id' => 123456789,
],
]);
$this->seeJson([
'meta' => [
'id' => 123456789,
],
]);
$this->assertResponseOk();
}
} }

View File

@@ -40,6 +40,7 @@ class CreateIncidentCommandTest extends AbstractTestCase
'occurred_at' => null, 'occurred_at' => null,
'template' => null, 'template' => null,
'template_vars' => [], 'template_vars' => [],
'meta' => [],
]; ];
$object = new CreateIncidentCommand( $object = new CreateIncidentCommand(
@@ -53,7 +54,8 @@ class CreateIncidentCommandTest extends AbstractTestCase
$params['stickied'], $params['stickied'],
$params['occurred_at'], $params['occurred_at'],
$params['template'], $params['template'],
$params['template_vars'] $params['template_vars'],
$params['meta']
); );
return compact('params', 'object'); return compact('params', 'object');

31
tests/Models/MetaTest.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Models;
use AltThree\TestBench\ValidationTrait;
use CachetHQ\Cachet\Models\Meta;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the meta model test class.
*
* @author James Brooks <james@alt-three.com>
*/
class MetaTest extends AbstractTestCase
{
use ValidationTrait;
public function testValidation()
{
$this->checkRules(new Meta());
}
}