mirror of
https://github.com/flarum/framework.git
synced 2025-01-19 07:42:48 +08:00
feat: add fieldsBefore
and fieldsAfter
ApiResource extenders (#4106)
This commit is contained in:
parent
c401e678f3
commit
3b69af2ae6
|
@ -25,6 +25,8 @@ class ApiResource implements ExtenderInterface
|
|||
private array $removeEndpoints = [];
|
||||
private array $endpoint = [];
|
||||
private array $fields = [];
|
||||
private array $fieldsBefore = [];
|
||||
private array $fieldsAfter = [];
|
||||
private array $removeFields = [];
|
||||
private array $field = [];
|
||||
private array $sorts = [];
|
||||
|
@ -93,6 +95,32 @@ class ApiResource implements ExtenderInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields to the resource before a certain field.
|
||||
*
|
||||
* @param string $before the name of the field to add the new fields before.
|
||||
* @param callable|class-string $fields must be a callable that returns an array of objects that implement \Tobyz\JsonApiServer\Schema\Field.
|
||||
*/
|
||||
public function fieldsBefore(string $before, callable|string $fields): self
|
||||
{
|
||||
$this->fieldsBefore[] = [$before, $fields];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields to the resource after a certain field.
|
||||
*
|
||||
* @param string $after the name of the field to add the new fields after.
|
||||
* @param callable|class-string $fields must be a callable that returns an array of objects that implement \Tobyz\JsonApiServer\Schema\Field.
|
||||
*/
|
||||
public function fieldsAfter(string $after, callable|string $fields): self
|
||||
{
|
||||
$this->fieldsAfter[] = [$after, $fields];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove fields from the resource.
|
||||
*
|
||||
|
@ -221,6 +249,26 @@ class ApiResource implements ExtenderInterface
|
|||
$fields = array_merge($fields, $newFieldsCallback());
|
||||
}
|
||||
|
||||
foreach ($this->fieldsBefore as [$before, $newFieldsCallback]) {
|
||||
$newFieldsCallback = ContainerUtil::wrapCallback($newFieldsCallback, $container);
|
||||
$newFields = $newFieldsCallback();
|
||||
$beforeIndex = array_search($before, array_column($fields, 'name'));
|
||||
|
||||
if ($beforeIndex !== false) {
|
||||
array_splice($fields, $beforeIndex, 0, $newFields);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->fieldsAfter as [$after, $newFieldsCallback]) {
|
||||
$newFieldsCallback = ContainerUtil::wrapCallback($newFieldsCallback, $container);
|
||||
$newFields = $newFieldsCallback();
|
||||
$afterIndex = array_search($after, array_column($fields, 'name'));
|
||||
|
||||
if ($afterIndex !== false) {
|
||||
array_splice($fields, $afterIndex + 1, 0, $newFields);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->removeFields as $field) {
|
||||
[$fieldsToRemove, $condition] = $field;
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@ use Flarum\Api\Endpoint\Index;
|
|||
use Flarum\Api\Endpoint\Show;
|
||||
use Flarum\Api\Resource\AbstractDatabaseResource;
|
||||
use Flarum\Api\Resource\DiscussionResource;
|
||||
use Flarum\Api\Resource\ForumResource;
|
||||
use Flarum\Api\Resource\UserResource;
|
||||
use Flarum\Api\Schema\Relationship\ToMany;
|
||||
use Flarum\Api\Schema;
|
||||
use Flarum\Api\Sort\SortColumn;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Extend;
|
||||
|
@ -25,11 +26,12 @@ use Flarum\Post\Post;
|
|||
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Testing\integration\TestCase;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\PostgresConnection;
|
||||
use Illuminate\Support\Arr;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tobyz\JsonApiServer\Schema\Field\Field;
|
||||
|
||||
class ApiControllerTest extends TestCase
|
||||
class ApiResourceTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
|
@ -45,15 +47,21 @@ class ApiControllerTest extends TestCase
|
|||
$this->normalUser()
|
||||
],
|
||||
Discussion::class => [
|
||||
['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],
|
||||
['id' => 1, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(1)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 2, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(2)->toDateTimeString(), 'user_id' => 3, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 3, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(3)->toDateTimeString(), 'user_id' => 1, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
|
||||
['id' => 4, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(4)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 5, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(5)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 6, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(6)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
],
|
||||
Post::class => [
|
||||
['id' => 1, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
['id' => 3, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
['id' => 3, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
|
||||
['id' => 5, 'discussion_id' => 6, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
@ -243,7 +251,7 @@ class ApiControllerTest extends TestCase
|
|||
->hasMany('customApiControllerRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
ToMany::make('customApiControllerRelation')
|
||||
Schema\Relationship\ToMany::make('customApiControllerRelation')
|
||||
->type('discussions')
|
||||
->includable(),
|
||||
])
|
||||
|
@ -271,7 +279,7 @@ class ApiControllerTest extends TestCase
|
|||
->hasMany('customApiControllerRelation2', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
ToMany::make('customApiControllerRelation2')
|
||||
Schema\Relationship\ToMany::make('customApiControllerRelation2')
|
||||
->type('discussions')
|
||||
->includable(),
|
||||
])
|
||||
|
@ -333,7 +341,7 @@ class ApiControllerTest extends TestCase
|
|||
->hasMany('customApiControllerRelation2', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
ToMany::make('customApiControllerRelation2')
|
||||
Schema\Relationship\ToMany::make('customApiControllerRelation2')
|
||||
->type('discussions')
|
||||
->includable(),
|
||||
])
|
||||
|
@ -362,7 +370,7 @@ class ApiControllerTest extends TestCase
|
|||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertCount(3, $payload['data']);
|
||||
$this->assertCount(6, $payload['data']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -444,7 +452,7 @@ class ApiControllerTest extends TestCase
|
|||
$payload = json_decode($body = $response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), $body);
|
||||
$this->assertEquals([3, 1, 2], Arr::pluck($payload['data'], 'id'));
|
||||
$this->assertEquals([3, 1, 4, 5, 6, 2], Arr::pluck($payload['data'], 'id'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -502,7 +510,12 @@ class ApiControllerTest extends TestCase
|
|||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals([2, 1, 3], Arr::pluck($payload['data'], 'id'));
|
||||
|
||||
if ($this->database() instanceof PostgresConnection) {
|
||||
$this->assertEquals([2, 1, 4, 5, 6, 3], Arr::pluck($payload['data'], 'id'));
|
||||
} else {
|
||||
$this->assertEquals([2, 6, 5, 4, 1, 3], Arr::pluck($payload['data'], 'id'));
|
||||
}
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -709,6 +722,326 @@ class ApiControllerTest extends TestCase
|
|||
|
||||
$this->assertFalse($users->pluck('firstLevelRelation')->filter->relationLoaded('secondLevelRelation')->isEmpty());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_dont_exist_by_default()
|
||||
{
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('customAttribute', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_exist_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(ForumResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Boolean::make('customAttribute')
|
||||
->get(fn () => true),
|
||||
])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_exist_if_added_before_field()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(DiscussionResource::class))
|
||||
->fieldsBefore('title', fn () => [
|
||||
Schema\Boolean::make('customAttribute')
|
||||
->writable()
|
||||
->set(fn (Discussion $discussion) => $this->assertNull($discussion->title))
|
||||
])
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('POST', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'Custom Discussion Title',
|
||||
'customAttribute' => true,
|
||||
'content' => 'Custom Discussion Content',
|
||||
],
|
||||
],
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode(), $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_exist_if_added_after_field()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(DiscussionResource::class))
|
||||
->fieldsAfter('title', fn () => [
|
||||
Schema\Boolean::make('customAttribute')
|
||||
->writable()
|
||||
->set(fn (Discussion $discussion) => $this->assertNotNull($discussion->title))
|
||||
])
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('POST', '/api/discussions', [
|
||||
'authenticatedAs' => 1,
|
||||
'json' => [
|
||||
'data' => [
|
||||
'type' => 'discussions',
|
||||
'attributes' => [
|
||||
'title' => 'Custom Discussion Title',
|
||||
'customAttribute' => true,
|
||||
'content' => 'Custom Discussion Content',
|
||||
],
|
||||
],
|
||||
],
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $response->getStatusCode(), $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_with_invokable_exist_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(ForumResource::class))
|
||||
->fields(CustomAttributesInvokableClass::class)
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttributeFromInvokable', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_exist_if_added_to_parent_class()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(AbstractDatabaseResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Boolean::make('customAttribute')
|
||||
->get(fn () => true),
|
||||
])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_prioritize_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(AbstractDatabaseResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('customAttribute')
|
||||
->get(fn () => 'initialValue')
|
||||
]),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('customAttribute')
|
||||
->get(fn () => 'newValue')
|
||||
]),
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||
$this->assertEquals('newValue', $payload['data']['attributes']['customAttribute']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_can_be_overridden()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('someCustomAttribute')
|
||||
->get(fn () => 'newValue'),
|
||||
])
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('someCustomAttribute')
|
||||
->get(fn () => 'secondValue'),
|
||||
Schema\Str::make('someOtherCustomAttribute')
|
||||
->get(fn () => 'secondValue'),
|
||||
])
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('someOtherCustomAttribute')
|
||||
->get(fn () => 'newValue'),
|
||||
])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('someCustomAttribute', $payload['data']['attributes']);
|
||||
$this->assertEquals('secondValue', $payload['data']['attributes']['someCustomAttribute']);
|
||||
$this->assertArrayHasKey('someOtherCustomAttribute', $payload['data']['attributes']);
|
||||
$this->assertEquals('newValue', $payload['data']['attributes']['someOtherCustomAttribute']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_relations_dont_exist_by_default()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->endpoint(Show::class, function (Show $endpoint): Show {
|
||||
return $endpoint->addDefaultInclude(['customSerializerRelation', 'postCustomRelation', 'anotherCustomRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_hasMany_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('customSerializerRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Relationship\ToMany::make('customSerializerRelation')
|
||||
->type('discussions')
|
||||
->includable()
|
||||
])
|
||||
->endpoint(Show::class, function (Show $endpoint) {
|
||||
return $endpoint->addDefaultInclude(['customSerializerRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$responseJson = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
|
||||
$this->assertCount(4, $responseJson['data']['relationships']['customSerializerRelation']['data']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_hasOne_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasOne('customSerializerRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Relationship\ToOne::make('customSerializerRelation')
|
||||
->type('discussions')
|
||||
->includable()
|
||||
])
|
||||
->endpoint(Show::class, function (Show $endpoint) {
|
||||
return $endpoint->addDefaultInclude(['customSerializerRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$responseJson = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
|
||||
$this->assertEquals('discussions', $responseJson['data']['relationships']['customSerializerRelation']['data']['type']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_relationship_is_inherited_to_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('anotherCustomRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(AbstractDatabaseResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Relationship\ToMany::make('anotherCustomRelation')
|
||||
->type('discussions')
|
||||
->includable()
|
||||
])
|
||||
->endpoint(Show::class, function (Show $endpoint) {
|
||||
return $endpoint->addDefaultInclude(['anotherCustomRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$responseJson = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('anotherCustomRelation', $responseJson['data']['relationships']);
|
||||
$this->assertCount(4, $responseJson['data']['relationships']['anotherCustomRelation']['data']);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomAfterEndpointInvokableClass
|
||||
|
@ -720,3 +1053,14 @@ class CustomAfterEndpointInvokableClass
|
|||
return $discussion;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomAttributesInvokableClass
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
Schema\Boolean::make('customAttributeFromInvokable')
|
||||
->get(fn () => true),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,320 +0,0 @@
|
|||
<?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\Endpoint\Show;
|
||||
use Flarum\Api\Resource\AbstractDatabaseResource;
|
||||
use Flarum\Api\Resource\ForumResource;
|
||||
use Flarum\Api\Resource\UserResource;
|
||||
use Flarum\Api\Schema;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Extend;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Testing\integration\TestCase;
|
||||
use Flarum\User\User;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
||||
class ApiSerializerTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->prepareDatabase([
|
||||
User::class => [
|
||||
$this->normalUser()
|
||||
],
|
||||
Discussion::class => [
|
||||
['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' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
['id' => 3, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
|
||||
],
|
||||
Post::class => [
|
||||
['id' => 1, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_dont_exist_by_default()
|
||||
{
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayNotHasKey('customAttribute', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_exist_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(ForumResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Boolean::make('customAttribute')
|
||||
->get(fn () => true),
|
||||
])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_with_invokable_exist_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(ForumResource::class))
|
||||
->fields(CustomAttributesInvokableClass::class)
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttributeFromInvokable', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_exist_if_added_to_parent_class()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(AbstractDatabaseResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Boolean::make('customAttribute')
|
||||
->get(fn () => true),
|
||||
])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_prioritize_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(AbstractDatabaseResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('customAttribute')
|
||||
->get(fn () => 'initialValue')
|
||||
]),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('customAttribute')
|
||||
->get(fn () => 'newValue')
|
||||
]),
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||
$this->assertEquals('newValue', $payload['data']['attributes']['customAttribute']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_attributes_can_be_overridden()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('someCustomAttribute')
|
||||
->get(fn () => 'newValue'),
|
||||
])
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('someCustomAttribute')
|
||||
->get(fn () => 'secondValue'),
|
||||
Schema\Str::make('someOtherCustomAttribute')
|
||||
->get(fn () => 'secondValue'),
|
||||
])
|
||||
->fields(fn () => [
|
||||
Schema\Str::make('someOtherCustomAttribute')
|
||||
->get(fn () => 'newValue'),
|
||||
])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$payload = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertArrayHasKey('someCustomAttribute', $payload['data']['attributes']);
|
||||
$this->assertEquals('secondValue', $payload['data']['attributes']['someCustomAttribute']);
|
||||
$this->assertArrayHasKey('someOtherCustomAttribute', $payload['data']['attributes']);
|
||||
$this->assertEquals('newValue', $payload['data']['attributes']['someOtherCustomAttribute']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_relations_dont_exist_by_default()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->endpoint(Show::class, function (Show $endpoint): Show {
|
||||
return $endpoint->addDefaultInclude(['customSerializerRelation', 'postCustomRelation', 'anotherCustomRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_hasMany_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('customSerializerRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Relationship\ToMany::make('customSerializerRelation')
|
||||
->type('discussions')
|
||||
->includable()
|
||||
])
|
||||
->endpoint(Show::class, function (Show $endpoint) {
|
||||
return $endpoint->addDefaultInclude(['customSerializerRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$responseJson = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
|
||||
$this->assertCount(3, $responseJson['data']['relationships']['customSerializerRelation']['data']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_hasOne_relationship_exists_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasOne('customSerializerRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Relationship\ToOne::make('customSerializerRelation')
|
||||
->type('discussions')
|
||||
->includable()
|
||||
])
|
||||
->endpoint(Show::class, function (Show $endpoint) {
|
||||
return $endpoint->addDefaultInclude(['customSerializerRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$responseJson = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
|
||||
$this->assertEquals('discussions', $responseJson['data']['relationships']['customSerializerRelation']['data']['type']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function custom_relationship_is_inherited_to_child_classes()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Model(User::class))
|
||||
->hasMany('anotherCustomRelation', Discussion::class, 'user_id'),
|
||||
(new Extend\ApiResource(AbstractDatabaseResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Relationship\ToMany::make('anotherCustomRelation')
|
||||
->type('discussions')
|
||||
->includable()
|
||||
])
|
||||
->endpoint(Show::class, function (Show $endpoint) {
|
||||
return $endpoint->addDefaultInclude(['anotherCustomRelation']);
|
||||
})
|
||||
);
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/users/2', [
|
||||
'authenticatedAs' => 1,
|
||||
])
|
||||
);
|
||||
|
||||
$responseJson = json_decode($response->getBody(), true);
|
||||
|
||||
$this->assertArrayHasKey('anotherCustomRelation', $responseJson['data']['relationships']);
|
||||
$this->assertCount(3, $responseJson['data']['relationships']['anotherCustomRelation']['data']);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomAttributesInvokableClass
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
Schema\Boolean::make('customAttributeFromInvokable')
|
||||
->get(fn () => true),
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user