mirror of
https://github.com/flarum/framework.git
synced 2025-02-07 12:44:00 +08:00
ApiController Extender and Tests (#2451)
This commit is contained in:
parent
0754de8d1b
commit
6d96587205
|
@ -82,6 +82,16 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
|||
*/
|
||||
protected static $events;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $beforeDataCallbacks = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $beforeSerializationCallbacks = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -89,12 +99,30 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
|||
{
|
||||
$document = new Document;
|
||||
|
||||
foreach (array_reverse(array_merge([static::class], class_parents($this))) as $class) {
|
||||
if (isset(static::$beforeDataCallbacks[$class])) {
|
||||
foreach (static::$beforeDataCallbacks[$class] as $callback) {
|
||||
$callback($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprected in beta 15, removed in beta 16
|
||||
static::$events->dispatch(
|
||||
new WillGetData($this)
|
||||
);
|
||||
|
||||
$data = $this->data($request, $document);
|
||||
|
||||
foreach (array_reverse(array_merge([static::class], class_parents($this))) as $class) {
|
||||
if (isset(static::$beforeSerializationCallbacks[$class])) {
|
||||
foreach (static::$beforeSerializationCallbacks[$class] as $callback) {
|
||||
$callback($this, $data, $request, $document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated in beta 15, removed in beta 16
|
||||
static::$events->dispatch(
|
||||
new WillSerializeData($this, $data, $request, $document)
|
||||
);
|
||||
|
@ -197,6 +225,106 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
|||
return new Parameters($request->getQueryParams());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the serializer that will serialize data for the endpoint.
|
||||
*
|
||||
* @param string $serializer
|
||||
*/
|
||||
public function setSerializer(string $serializer)
|
||||
{
|
||||
$this->serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the given relationship by default.
|
||||
*
|
||||
* @param string|array $name
|
||||
*/
|
||||
public function addInclude($name)
|
||||
{
|
||||
$this->include = array_merge($this->include, (array) $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't include the given relationship by default.
|
||||
*
|
||||
* @param string|array $name
|
||||
*/
|
||||
public function removeInclude($name)
|
||||
{
|
||||
$this->include = array_diff($this->include, (array) $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given relationship available for inclusion.
|
||||
*
|
||||
* @param string|array $name
|
||||
*/
|
||||
public function addOptionalInclude($name)
|
||||
{
|
||||
$this->optionalInclude = array_merge($this->optionalInclude, (array) $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't allow the given relationship to be included.
|
||||
*
|
||||
* @param string|array $name
|
||||
*/
|
||||
public function removeOptionalInclude($name)
|
||||
{
|
||||
$this->optionalInclude = array_diff($this->optionalInclude, (array) $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default number of results.
|
||||
*
|
||||
* @param int $limit
|
||||
*/
|
||||
public function setLimit(int $limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of results.
|
||||
*
|
||||
* @param int $max
|
||||
*/
|
||||
public function setMaxLimit(int $max)
|
||||
{
|
||||
$this->maxLimit = $max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sorting results by the given field.
|
||||
*
|
||||
* @param string|array $field
|
||||
*/
|
||||
public function addSortField($field)
|
||||
{
|
||||
$this->sortFields = array_merge($this->sortFields, (array) $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallow sorting results by the given field.
|
||||
*
|
||||
* @param string|array $field
|
||||
*/
|
||||
public function removeSortField($field)
|
||||
{
|
||||
$this->sortFields = array_diff($this->sortFields, (array) $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default sort order for the results.
|
||||
*
|
||||
* @param array $sort
|
||||
*/
|
||||
public function setSort(array $sort)
|
||||
{
|
||||
$this->sort = $sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Dispatcher
|
||||
*/
|
||||
|
@ -228,4 +356,30 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
|||
{
|
||||
static::$container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controllerClass
|
||||
* @param callable $callback
|
||||
*/
|
||||
public static function addDataPreparationCallback(string $controllerClass, callable $callback)
|
||||
{
|
||||
if (! isset(static::$beforeDataCallbacks[$controllerClass])) {
|
||||
static::$beforeDataCallbacks[$controllerClass] = [];
|
||||
}
|
||||
|
||||
static::$beforeDataCallbacks[$controllerClass][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controllerClass
|
||||
* @param callable $callback
|
||||
*/
|
||||
public static function addSerializationPreparationCallback(string $controllerClass, callable $callback)
|
||||
{
|
||||
if (! isset(static::$beforeSerializationCallbacks[$controllerClass])) {
|
||||
static::$beforeSerializationCallbacks[$controllerClass] = [];
|
||||
}
|
||||
|
||||
static::$beforeSerializationCallbacks[$controllerClass][] = $callback;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ namespace Flarum\Api\Event;
|
|||
use Flarum\Api\Controller\AbstractSerializeController;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* @deprecated in beta 15, removed in beta 16
|
||||
*/
|
||||
class WillGetData
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,9 @@ use Flarum\Api\Controller\AbstractSerializeController;
|
|||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
/**
|
||||
* @deprecated in beta 15, removed in beta 16
|
||||
*/
|
||||
class WillSerializeData
|
||||
{
|
||||
/**
|
||||
|
|
302
framework/core/src/Extend/ApiController.php
Normal file
302
framework/core/src/Extend/ApiController.php
Normal file
|
@ -0,0 +1,302 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Extend;
|
||||
|
||||
use Flarum\Api\Controller\AbstractSerializeController;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Foundation\ContainerUtil;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class ApiController implements ExtenderInterface
|
||||
{
|
||||
private $controllerClass;
|
||||
private $beforeDataCallbacks = [];
|
||||
private $beforeSerializationCallbacks = [];
|
||||
private $serializer;
|
||||
private $addIncludes = [];
|
||||
private $removeIncludes = [];
|
||||
private $addOptionalIncludes = [];
|
||||
private $removeOptionalIncludes = [];
|
||||
private $limit;
|
||||
private $maxLimit;
|
||||
private $addSortFields = [];
|
||||
private $removeSortFields = [];
|
||||
private $sort;
|
||||
|
||||
/**
|
||||
* @param string $controllerClass The ::class attribute of the controller you are modifying.
|
||||
* This controller should extend from \Flarum\Api\Controller\AbstractSerializeController.
|
||||
*/
|
||||
public function __construct(string $controllerClass)
|
||||
{
|
||||
$this->controllerClass = $controllerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|string $callback
|
||||
*
|
||||
* The callback can be a closure or an invokable class, and should accept:
|
||||
* - $controller: An instance of this controller.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function prepareDataQuery($callback)
|
||||
{
|
||||
$this->beforeDataCallbacks[] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|string $callback
|
||||
*
|
||||
* The callback can be a closure or an invokable class, and should accept:
|
||||
* - $controller: An instance of this controller.
|
||||
* - $data: Mixed, can be an array of data or an object (like an instance of Collection or AbstractModel).
|
||||
* - $request: An instance of \Psr\Http\Message\ServerRequestInterface.
|
||||
* - $document: An instance of \Tobscure\JsonApi\Document.
|
||||
*
|
||||
* The callable should return:
|
||||
* - An array of additional data to merge with the existing array.
|
||||
* Or a modified $data array.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function prepareDataForSerialization($callback)
|
||||
{
|
||||
$this->beforeSerializationCallbacks[] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the serializer that will serialize data for the endpoint.
|
||||
*
|
||||
* @param string $serializerClass
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function setSerializer(string $serializerClass, $callback = null)
|
||||
{
|
||||
$this->serializer = [$serializerClass, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the given relationship by default.
|
||||
*
|
||||
* @param string|array $name
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function addInclude($name, $callback = null)
|
||||
{
|
||||
$this->addIncludes[] = [$name, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't include the given relationship by default.
|
||||
*
|
||||
* @param string|array $name
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function removeInclude($name, $callback = null)
|
||||
{
|
||||
$this->removeIncludes[] = [$name, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given relationship available for inclusion.
|
||||
*
|
||||
* @param string|array $name
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function addOptionalInclude($name, $callback = null)
|
||||
{
|
||||
$this->addOptionalIncludes[] = [$name, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't allow the given relationship to be included.
|
||||
*
|
||||
* @param string|array $name
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function removeOptionalInclude($name, $callback = null)
|
||||
{
|
||||
$this->removeOptionalIncludes[] = [$name, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default number of results.
|
||||
*
|
||||
* @param int $limit
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function setLimit(int $limit, $callback = null)
|
||||
{
|
||||
$this->limit = [$limit, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of results.
|
||||
*
|
||||
* @param int $max
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function setMaxLimit(int $max, $callback = null)
|
||||
{
|
||||
$this->maxLimit = [$max, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow sorting results by the given field.
|
||||
*
|
||||
* @param string|array $field
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function addSortField($field, $callback = null)
|
||||
{
|
||||
$this->addSortFields[] = [$field, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallow sorting results by the given field.
|
||||
*
|
||||
* @param string|array $field
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function removeSortField($field, $callback = null)
|
||||
{
|
||||
$this->removeSortFields[] = [$field, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default sort order for the results.
|
||||
*
|
||||
* @param array $sort
|
||||
* @param callable|string|null $callback
|
||||
* @return self
|
||||
*/
|
||||
public function setSort(array $sort, $callback = null)
|
||||
{
|
||||
$this->sort = [$sort, $callback];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
$this->beforeDataCallbacks[] = function (AbstractSerializeController $controller) use ($container) {
|
||||
if (isset($this->serializer) && $this->isApplicable($this->serializer[1], $controller, $container)) {
|
||||
$controller->setSerializer($this->serializer[0]);
|
||||
}
|
||||
|
||||
foreach ($this->addIncludes as $addingInclude) {
|
||||
if ($this->isApplicable($addingInclude[1], $controller, $container)) {
|
||||
$controller->addInclude($addingInclude[0]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->removeIncludes as $removingInclude) {
|
||||
if ($this->isApplicable($removingInclude[1], $controller, $container)) {
|
||||
$controller->removeInclude($removingInclude[0]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->addOptionalIncludes as $addingOptionalInclude) {
|
||||
if ($this->isApplicable($addingOptionalInclude[1], $controller, $container)) {
|
||||
$controller->addOptionalInclude($addingOptionalInclude[0]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->removeOptionalIncludes as $removingOptionalInclude) {
|
||||
if ($this->isApplicable($removingOptionalInclude[1], $controller, $container)) {
|
||||
$controller->removeOptionalInclude($removingOptionalInclude[0]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->addSortFields as $addingSortField) {
|
||||
if ($this->isApplicable($addingSortField[1], $controller, $container)) {
|
||||
$controller->addSortField($addingSortField[0]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->removeSortFields as $removingSortField) {
|
||||
if ($this->isApplicable($removingSortField[1], $controller, $container)) {
|
||||
$controller->removeSortField($removingSortField[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->limit) && $this->isApplicable($this->limit[1], $controller, $container)) {
|
||||
$controller->setLimit($this->limit[0]);
|
||||
}
|
||||
|
||||
if (isset($this->maxLimit) && $this->isApplicable($this->maxLimit[1], $controller, $container)) {
|
||||
$controller->setMaxLimit($this->maxLimit[0]);
|
||||
}
|
||||
|
||||
if (isset($this->sort) && $this->isApplicable($this->sort[1], $controller, $container)) {
|
||||
$controller->setSort($this->sort[0]);
|
||||
}
|
||||
};
|
||||
|
||||
foreach ($this->beforeDataCallbacks as $beforeDataCallback) {
|
||||
$beforeDataCallback = ContainerUtil::wrapCallback($beforeDataCallback, $container);
|
||||
AbstractSerializeController::addDataPreparationCallback($this->controllerClass, $beforeDataCallback);
|
||||
}
|
||||
|
||||
foreach ($this->beforeSerializationCallbacks as $beforeSerializationCallback) {
|
||||
$beforeSerializationCallback = ContainerUtil::wrapCallback($beforeSerializationCallback, $container);
|
||||
AbstractSerializeController::addSerializationPreparationCallback($this->controllerClass, $beforeSerializationCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|string|null $callback
|
||||
* @param AbstractSerializeController $controller
|
||||
* @param Container $container
|
||||
* @return bool
|
||||
*/
|
||||
private function isApplicable($callback, AbstractSerializeController $controller, Container $container)
|
||||
{
|
||||
if (! isset($callback)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$callback = ContainerUtil::wrapCallback($callback, $container);
|
||||
|
||||
return (bool) $callback($controller);
|
||||
}
|
||||
}
|
773
framework/core/tests/integration/extenders/ApiControllerTest.php
Normal file
773
framework/core/tests/integration/extenders/ApiControllerTest.php
Normal file
|
@ -0,0 +1,773 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Tests\integration\extenders;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Api\Controller\AbstractShowController;
|
||||
use Flarum\Api\Controller\ListDiscussionsController;
|
||||
use Flarum\Api\Controller\ShowDiscussionController;
|
||||
use Flarum\Api\Controller\ShowForumController;
|
||||
use Flarum\Api\Controller\ShowPostController;
|
||||
use Flarum\Api\Controller\ShowUserController;
|
||||
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||
use Flarum\Api\Serializer\ForumSerializer;
|
||||
use Flarum\Api\Serializer\PostSerializer;
|
||||
use Flarum\Api\Serializer\UserSerializer;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Extend;
|
||||
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class ApiControllerTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
protected function prepDb()
|
||||
{
|
||||
$this->prepareDatabase([
|
||||
'users' => [
|
||||
$this->adminUser(),
|
||||
$this->normalUser()
|
||||
],
|
||||
'groups' => [
|
||||
$this->adminGroup(),
|
||||
$this->memberGroup()
|
||||
],
|
||||
'discussions' => [
|
||||
['id' => 1, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 2, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 3, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 3, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_serialization_callback_works_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ShowDiscussionController::class))
|
||||
->prepareDataForSerialization(function ($controller, Discussion $discussion) {
|
||||
$discussion->title = 'dataSerializationPrepCustomTitle';
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertEquals('dataSerializationPrepCustomTitle', $payload['data']['attributes']['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_serialization_callback_works_with_invokable_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ShowDiscussionController::class))
|
||||
->prepareDataForSerialization(CustomPrepareDataSerializationInvokableClass::class)
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertEquals(CustomPrepareDataSerializationInvokableClass::class, $payload['data']['attributes']['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_serialization_allows_passing_args_by_reference_with_closures()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||
->hasMany('referenceTest', UserSerializer::class),
|
||||
(new Extend\ApiController(ShowForumController::class))
|
||||
->addInclude('referenceTest')
|
||||
->prepareDataForSerialization(function ($controller, &$data) {
|
||||
$data['referenceTest'] = User::limit(2)->get();
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('referenceTest', $payload['data']['relationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_serialization_allows_passing_args_by_reference_with_invokable_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||
->hasMany('referenceTest2', UserSerializer::class),
|
||||
(new Extend\ApiController(ShowForumController::class))
|
||||
->addInclude('referenceTest2')
|
||||
->prepareDataForSerialization(CustomInvokableClassArgsReference::class)
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('referenceTest2', $payload['data']['relationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_serialization_callback_works_if_added_to_parent_class()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(AbstractShowController::class))
|
||||
->prepareDataForSerialization(function ($controller, Discussion $discussion) {
|
||||
if ($controller instanceof ShowDiscussionController) {
|
||||
$discussion->title = 'dataSerializationPrepCustomTitle2';
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertEquals('dataSerializationPrepCustomTitle2', $payload['data']['attributes']['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_serialization_callback_prioritizes_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(AbstractShowController::class))
|
||||
->prepareDataForSerialization(function ($controller, Discussion $discussion) {
|
||||
if ($controller instanceof ShowDiscussionController) {
|
||||
$discussion->title = 'dataSerializationPrepCustomTitle3';
|
||||
}
|
||||
}),
|
||||
(new Extend\ApiController(ShowDiscussionController::class))
|
||||
->prepareDataForSerialization(function ($controller, Discussion $discussion) {
|
||||
$discussion->title = 'dataSerializationPrepCustomTitle4';
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertEquals('dataSerializationPrepCustomTitle4', $payload['data']['attributes']['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_query_callback_works_if_added_to_parent_class()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(AbstractShowController::class))
|
||||
->prepareDataQuery(function ($controller) {
|
||||
if ($controller instanceof ShowDiscussionController) {
|
||||
$controller->setSerializer(CustomDiscussionSerializer2::class);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializer2', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function prepare_data_query_callback_prioritizes_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(AbstractShowController::class))
|
||||
->prepareDataForSerialization(function ($controller) {
|
||||
if ($controller instanceof ShowDiscussionController) {
|
||||
$controller->setSerializer(CustomDiscussionSerializer2::class);
|
||||
}
|
||||
}),
|
||||
(new Extend\ApiController(ShowDiscussionController::class))
|
||||
->prepareDataForSerialization(function ($controller) {
|
||||
$controller->setSerializer(CustomDiscussionSerializer::class);
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializer', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_serializer_doesnt_work_by_default()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('customSerializer', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_serializer_works_if_set()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ShowDiscussionController::class))
|
||||
->setSerializer(CustomDiscussionSerializer::class)
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializer', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_serializer_works_if_set_with_invokable_class()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ShowPostController::class))
|
||||
->setSerializer(CustomPostSerializer::class, CustomApiControllerInvokableClass::class)
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
$this->prepareDatabase([
|
||||
'posts' => [
|
||||
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '<t><p>foo bar</p></t>'],
|
||||
],
|
||||
]);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/posts/1', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializer', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_serializer_doesnt_work_with_false_callback_return()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ShowUserController::class))
|
||||
->setSerializer(CustomUserSerializer::class, function () {
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('customSerializer', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_not_included_by_default()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('customApiControllerRelation', $payload['data']['relationships']);
|
||||
$this->assertArrayNotHasKey('customApiControllerRelation2', $payload['data']['relationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_included_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('customApiControllerRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiSerializer(UserSerializer::class))
|
||||
->hasMany('customApiControllerRelation', DiscussionSerializer::class),
|
||||
(new Extend\ApiController(ShowUserController::class))
|
||||
->addInclude('customApiControllerRelation')
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customApiControllerRelation', $payload['data']['relationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_optionally_included_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('customApiControllerRelation2', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiSerializer(UserSerializer::class))
|
||||
->hasMany('customApiControllerRelation2', DiscussionSerializer::class),
|
||||
(new Extend\ApiController(ShowUserController::class))
|
||||
->addOptionalInclude('customApiControllerRelation2')
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'include' => 'customApiControllerRelation2',
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customApiControllerRelation2', $payload['data']['relationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_included_by_default()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('groups', $payload['data']['relationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_not_included_if_removed()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ShowUserController::class))
|
||||
->removeInclude('groups')
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('groups', Arr::get($payload, 'data.relationships', []));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_relationship_not_optionally_included_if_removed()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('customApiControllerRelation2', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiSerializer(UserSerializer::class))
|
||||
->hasMany('customApiControllerRelation2', DiscussionSerializer::class),
|
||||
(new Extend\ApiController(ShowUserController::class))
|
||||
->addOptionalInclude('customApiControllerRelation2')
|
||||
->removeOptionalInclude('customApiControllerRelation2')
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'include' => 'customApiControllerRelation2',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_limit_doesnt_work_by_default()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertCount(3, $payload['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_limit_works_if_set()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ListDiscussionsController::class))
|
||||
->setLimit(1)
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertCount(1, $payload['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_max_limit_works_if_set()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ListDiscussionsController::class))
|
||||
->setMaxLimit(1)
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'page' => ['limit' => '5'],
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertCount(1, $payload['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_sort_field_doesnt_exist_by_default()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'sort' => 'userId',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_sort_field_doesnt_work_with_false_callback_return()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ListDiscussionsController::class))
|
||||
->addSortField('userId', function () {
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'sort' => 'userId',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_sort_field_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ListDiscussionsController::class))
|
||||
->addSortField('userId')
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'sort' => 'userId',
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals([3, 1, 2], Arr::pluck($payload['data'], 'id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_sort_field_exists_by_default()
|
||||
{
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'sort' => 'createdAt',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_sort_field_doesnt_exist_if_removed()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ListDiscussionsController::class))
|
||||
->removeSortField('createdAt')
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])->withQueryParams([
|
||||
'sort' => 'createdAt',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function custom_sort_field_works_if_set()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiController(ListDiscussionsController::class))
|
||||
->addSortField('userId')
|
||||
->setSort(['userId' => 'desc'])
|
||||
);
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals([2, 1, 3], Arr::pluck($payload['data'], 'id'));
|
||||
}
|
||||
}
|
||||
|
||||
class CustomDiscussionSerializer extends DiscussionSerializer
|
||||
{
|
||||
protected function getDefaultAttributes($discussion)
|
||||
{
|
||||
return parent::getDefaultAttributes($discussion) + [
|
||||
'customSerializer' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class CustomDiscussionSerializer2 extends DiscussionSerializer
|
||||
{
|
||||
protected function getDefaultAttributes($discussion)
|
||||
{
|
||||
return parent::getDefaultAttributes($discussion) + [
|
||||
'customSerializer2' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class CustomUserSerializer extends UserSerializer
|
||||
{
|
||||
protected function getDefaultAttributes($user)
|
||||
{
|
||||
return parent::getDefaultAttributes($user) + [
|
||||
'customSerializer' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class CustomPostSerializer extends PostSerializer
|
||||
{
|
||||
protected function getDefaultAttributes($post)
|
||||
{
|
||||
return parent::getDefaultAttributes($post) + [
|
||||
'customSerializer' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class CustomApiControllerInvokableClass
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomPrepareDataSerializationInvokableClass
|
||||
{
|
||||
public function __invoke(ShowDiscussionController $controller, Discussion $discussion)
|
||||
{
|
||||
$discussion->title = __CLASS__;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomInvokableClassArgsReference
|
||||
{
|
||||
public function __invoke($controller, &$data)
|
||||
{
|
||||
$data['referenceTest2'] = User::limit(2)->get();
|
||||
}
|
||||
}
|
|
@ -85,6 +85,8 @@ class ThrottleApiTest extends TestCase
|
|||
|
||||
$this->prepDb();
|
||||
|
||||
$this->prepDb();
|
||||
|
||||
$response = $this->send($this->request('GET', '/api/discussions', ['authenticatedAs' => 2]));
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
|
Loading…
Reference in New Issue
Block a user