Introduce the Subscriber API. Closes #787

This commit is contained in:
James Brooks 2015-07-14 14:09:32 +01:00
parent 38257c6ae6
commit 71f5de8726
10 changed files with 338 additions and 5 deletions

View File

@ -0,0 +1,77 @@
<?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\Http\Controllers\Api;
use CachetHQ\Cachet\Events\CustomerHasSubscribedEvent;
use CachetHQ\Cachet\Models\Subscriber;
use Exception;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class SubscriberController extends AbstractApiController
{
/**
* Get all subscribers.
*
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function getSubscribers(Request $request)
{
$subscribers = Subscriber::paginate(Binput::get('per_page', 20));
return $this->paginator($subscribers, $request);
}
/**
* Create a new subscriber.
*
* @return \CachetHQ\Cachet\Models\Subscriber
*/
public function postSubscribers()
{
$subscriberData = Binput::except('verify');
try {
$subscriber = Subscriber::create($subscriberData);
} catch (Exception $e) {
throw new BadRequestHttpException();
}
if ($subscriber->isValid()) {
// If we're auto-verifying the subscriber, don't bother with this event.
if (!(Binput::get('verify'))) {
event(new CustomerHasSubscribedEvent($subscriber));
}
return $this->item($subscriber);
}
throw new BadRequestHttpException();
}
/**
* Delete a subscriber.
*
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
*
* @return \Illuminate\Http\JsonResponse
*/
public function deleteSubscriber(Subscriber $subscriber)
{
$subscriber->delete();
return $this->noContent();
}
}

View File

@ -45,10 +45,13 @@ class ApiRoutes
// Api protected
$router->group(['middleware' => 'auth.api'], function ($router) {
$router->get('subscribers', 'SubscriberController@getSubscribers');
$router->post('components', 'ComponentController@postComponents');
$router->post('incidents', 'IncidentController@postIncidents');
$router->post('metrics', 'MetricController@postMetrics');
$router->post('metrics/{metric}/points', 'MetricPointController@postMetricPoints');
$router->post('subscribers', 'SubscriberController@postSubscribers');
$router->put('components/{component}', 'ComponentController@putComponent');
$router->put('incidents/{incident}', 'IncidentController@putIncident');
@ -59,6 +62,7 @@ class ApiRoutes
$router->delete('incidents/{incident}', 'IncidentController@deleteIncident');
$router->delete('metrics/{metric}', 'MetricController@deleteMetric');
$router->delete('metrics/{metric}/points/{metric_point}', 'MetricPointController@deleteMetricPoint');
$router->delete('subscribers/{subscriber}', 'SubscriberController@deleteSubscriber');
});
});
}

View File

@ -11,11 +11,13 @@
namespace CachetHQ\Cachet\Models;
use CachetHQ\Cachet\Presenters\SubscriberPresenter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use McCool\LaravelAutoPresenter\HasPresenter;
use Watson\Validating\ValidatingTrait;
class Subscriber extends Model
class Subscriber extends Model implements HasPresenter
{
use SoftDeletes, ValidatingTrait;
@ -75,4 +77,14 @@ class Subscriber extends Model
{
return str_random(42);
}
/**
* Get the presenter class.
*
* @return string
*/
public function getPresenterClass()
{
return SubscriberPresenter::class;
}
}

View File

@ -0,0 +1,33 @@
<?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\Presenters;
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
class SubscriberPresenter extends AbstractPresenter
{
use TimestampsTrait;
/**
* Convert the presenter instance to an array.
*
* @return string[]
*/
public function toArray()
{
return array_merge($this->wrappedObject->toArray(), [
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
'verified_at' => $this->verified_at(),
]);
}
}

View File

@ -47,4 +47,15 @@ trait TimestampsTrait
return (new Date($this->wrappedObject->deleted_at))
->setTimezone($this->setting->get('app_timezone'))->toDateTimeString();
}
/**
* Present formatted date time.
*
* @return string
*/
public function verified_at()
{
return (new Date($this->wrappedObject->verified_at))
->setTimezone($this->setting->get('app_timezone'))->toDateTimeString();
}
}

View File

@ -39,7 +39,8 @@
"require-dev": {
"fzaninotto/faker": "^1.5",
"phpunit/phpunit": "^4.7.6",
"laravel/homestead": "^2.1"
"laravel/homestead": "^2.1",
"mockery/mockery": "~0.9.4"
},
"autoload": {
"classmap": [

114
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "94c9c02a89e02e83d24580aa8ac901bf",
"hash": "8535c80a786ed3bcac0a4d7d77421697",
"packages": [
{
"name": "alt-three/segment",
@ -1546,7 +1546,7 @@
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia",
"role": "developer"
"role": "Developer"
}
],
"description": "Serialize Closure objects, including their context and binding",
@ -3376,6 +3376,51 @@
],
"time": "2015-05-29 06:29:14"
},
{
"name": "hamcrest/hamcrest-php",
"version": "v1.2.2",
"source": {
"type": "git",
"url": "https://github.com/hamcrest/hamcrest-php.git",
"reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c",
"reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"replace": {
"cordoval/hamcrest-php": "*",
"davedevelopment/hamcrest-php": "*",
"kodova/hamcrest-php": "*"
},
"require-dev": {
"phpunit/php-file-iterator": "1.3.3",
"satooshi/php-coveralls": "dev-master"
},
"type": "library",
"autoload": {
"classmap": [
"hamcrest"
],
"files": [
"hamcrest/Hamcrest.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"description": "This is the PHP port of Hamcrest Matchers",
"keywords": [
"test"
],
"time": "2015-05-11 14:41:42"
},
{
"name": "laravel/homestead",
"version": "v2.1.5",
@ -3417,6 +3462,71 @@
"description": "A virtual machine for web artisans.",
"time": "2015-07-23 14:53:43"
},
{
"name": "mockery/mockery",
"version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/padraic/mockery.git",
"reference": "70bba85e4aabc9449626651f48b9018ede04f86b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b",
"reference": "70bba85e4aabc9449626651f48b9018ede04f86b",
"shasum": ""
},
"require": {
"hamcrest/hamcrest-php": "~1.1",
"lib-pcre": ">=7.0",
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.9.x-dev"
}
},
"autoload": {
"psr-0": {
"Mockery": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Pádraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "http://blog.astrumfutura.com"
},
{
"name": "Dave Marshall",
"email": "dave.marshall@atstsolutions.co.uk",
"homepage": "http://davedevelopment.co.uk"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
"homepage": "http://github.com/padraic/mockery",
"keywords": [
"BDD",
"TDD",
"library",
"mock",
"mock objects",
"mockery",
"stub",
"test",
"test double",
"testing"
],
"time": "2015-04-02 19:54:00"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "2.0.4",

View File

@ -9,6 +9,8 @@
* file that was distributed with this source code.
*/
use Carbon\Carbon;
$factory->define('CachetHQ\Cachet\Models\User', function ($faker) {
return [
'username' => $faker->userName,
@ -57,3 +59,11 @@ $factory->define('CachetHQ\Cachet\Models\MetricPoint', function ($faker) {
'value' => rand(1, 100),
];
});
$factory->define('CachetHQ\Cachet\Models\Subscriber', function ($faker) {
return [
'email' => $faker->email,
'verify_code' => 'Mqr80r2wJtxHCW5Ep4azkldFfIwHhw98M9HF04dn0z',
'verified_at' => Carbon::now(),
];
});

View File

@ -26,7 +26,7 @@
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_URL" value="http://localhost"/>
<env name="APP_KEY" value="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/>
<env name="APP_KEY" value="GCvcgDKMRIN498g52zfVEd9CxDs6PR7q"/>
<env name="DB_DRIVER" value="sqlite"/>
<env name="DB_HOST" value=":memory:"/>
<env name="CACHE_DRIVER" value="array"/>

View File

@ -0,0 +1,75 @@
<?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\Api;
use CachetHQ\Tests\Cachet\AbstractTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
// use Illuminate\Foundation\Testing\WithoutEvents;
class SubscriberTest extends AbstractTestCase
{
use DatabaseMigrations;
public function testGetSubscribersUnauthenticated()
{
$this->get('/api/v1/subscribers');
$this->assertResponseStatus(401);
$this->seeHeader('Content-Type', 'application/json');
}
public function testGetSubscribers()
{
$this->beUser();
$subscriber = factory('CachetHQ\Cachet\Models\Subscriber')->create();
$this->get('/api/v1/subscribers');
$this->seeHeader('Content-Type', 'application/json');
$this->assertResponseOk();
}
public function testCreateSubscriber()
{
$this->beUser();
$this->expectsEvents('CachetHQ\Cachet\Events\CustomerHasSubscribedEvent');
$this->post('/api/v1/subscribers', [
'email' => 'james@cachethq.io',
]);
$this->assertResponseOk();
$this->seeHeader('Content-Type', 'application/json');
$this->seeJson(['email' => 'james@cachethq.io']);
}
public function testCreateSubscriberAutoVerified()
{
$this->beUser();
$this->post('/api/v1/subscribers', [
'email' => 'james@cachethq.io',
'verify' => true,
]);
$this->assertResponseOk();
$this->seeHeader('Content-Type', 'application/json');
$this->seeJson(['email' => 'james@cachethq.io']);
}
public function testDeleteSubscriber()
{
$this->beUser();
$subscriber = factory('CachetHQ\Cachet\Models\Subscriber')->create();
$this->delete("/api/v1/subscribers/{$subscriber->id}");
$this->assertResponseStatus(204);
}
}