Skip to content

Commit 6780af1

Browse files
committed
feat: allow adding endpoints before/after others
1 parent 4feb4a3 commit 6780af1

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

framework/core/src/Api/Endpoint/Endpoint.php

+4
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ public function handle(\Tobyz\JsonApiServer\Context $context): ?Response
148148
return json_api_response($this->showResource($context, $data));
149149
}
150150

151+
if (is_array($data)) {
152+
return json_api_response($data);
153+
}
154+
151155
return null;
152156
}
153157
}

framework/core/src/Extend/ApiResource.php

+66
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
class ApiResource implements ExtenderInterface
2323
{
2424
private array $endpoints = [];
25+
private array $endpointsBefore = [];
26+
private array $endpointsAfter = [];
2527
private array $removeEndpoints = [];
2628
private array $endpoint = [];
2729
private array $fields = [];
@@ -55,6 +57,45 @@ public function endpoints(callable|string $endpoints): self
5557
return $this;
5658
}
5759

60+
/**
61+
* Add endpoints to the resource before a certain endpoint.
62+
*
63+
* @param string $before the name of the endpoint to add the new endpoints before.
64+
* @param callable|class-string $endpoints must be a callable that returns an array of objects that implement \Flarum\Api\Endpoint\Endpoint.
65+
*/
66+
public function endpointsBefore(string $before, callable|string $endpoints): self
67+
{
68+
$this->endpointsBefore[] = [$before, $endpoints];
69+
70+
return $this;
71+
}
72+
73+
/**
74+
* Add endpoints to the resource after a certain endpoint.
75+
*
76+
* @param string $after the name of the endpoint to add the new endpoints after.
77+
* @param callable|class-string $endpoints must be a callable that returns an array of objects that implement \Flarum\Api\Endpoint\Endpoint.
78+
*/
79+
public function endpointsAfter(string $after, callable|string $endpoints): self
80+
{
81+
$this->endpointsAfter[] = [$after, $endpoints];
82+
83+
return $this;
84+
}
85+
86+
/**
87+
* Add endpoints to the resource before all other endpoints.
88+
*
89+
* @param string $after the name of the endpoint to add the new endpoints after.
90+
* @param callable|class-string $endpoints must be a callable that returns an array of objects that implement \Flarum\Api\Endpoint\Endpoint.
91+
*/
92+
public function endpointsBeforeAll(callable|string $endpoints): self
93+
{
94+
$this->endpointsBefore[] = [0, $endpoints];
95+
96+
return $this;
97+
}
98+
5899
/**
59100
* Remove endpoints from the resource.
60101
*
@@ -214,6 +255,31 @@ function (array $endpoints, Resource $resource) use ($container): array {
214255
$endpoints = array_merge($endpoints, $newEndpointsCallback());
215256
}
216257

258+
foreach ($this->endpointsBefore as [$before, $newEndpointsCallback]) {
259+
$newEndpointsCallback = ContainerUtil::wrapCallback($newEndpointsCallback, $container);
260+
261+
if ($before === 0) {
262+
array_unshift($endpoints, ...$newEndpointsCallback());
263+
} else {
264+
$newEndpoints = $newEndpointsCallback();
265+
$beforeIndex = array_search($before, array_column($endpoints, 'name'));
266+
267+
if ($beforeIndex !== false) {
268+
array_splice($endpoints, $beforeIndex, 0, $newEndpoints);
269+
}
270+
}
271+
}
272+
273+
foreach ($this->endpointsAfter as [$after, $newEndpointsCallback]) {
274+
$newEndpointsCallback = ContainerUtil::wrapCallback($newEndpointsCallback, $container);
275+
$newEndpoints = $newEndpointsCallback();
276+
$afterIndex = array_search($after, array_column($endpoints, 'name'));
277+
278+
if ($afterIndex !== false) {
279+
array_splice($endpoints, $afterIndex + 1, 0, $newEndpoints);
280+
}
281+
}
282+
217283
foreach ($this->removeEndpoints as $removeEndpointClass) {
218284
[$endpointsToRemove, $condition] = $removeEndpointClass;
219285

framework/core/tests/integration/extenders/ApiResourceTest.php

+61
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use Carbon\Carbon;
1313
use Flarum\Api\Context;
14+
use Flarum\Api\Endpoint\Endpoint;
1415
use Flarum\Api\Endpoint\Index;
1516
use Flarum\Api\Endpoint\Show;
1617
use Flarum\Api\Resource\AbstractDatabaseResource;
@@ -91,6 +92,66 @@ public function after_endpoint_callback_works_if_added()
9192
$this->assertEquals('dataSerializationPrepCustomTitle', $payload['data']['attributes']['title'], $body);
9293
}
9394

95+
#[Test]
96+
public function custom_endpoint_works_if_added()
97+
{
98+
$this->extend(
99+
(new Extend\ApiResource(DiscussionResource::class))
100+
->endpoints(fn () => [
101+
Endpoint::make('custom')
102+
->route('GET', '/{id}/custom')
103+
->action(function (Context $context) {
104+
$discussion = $context->model;
105+
106+
return [
107+
'data' => [
108+
'message' => 'custom endpoint '.$discussion->id,
109+
],
110+
];
111+
}),
112+
])
113+
);
114+
115+
$response = $this->send(
116+
$this->request('GET', '/api/discussions/1/custom', [
117+
'authenticatedAs' => 1,
118+
])
119+
);
120+
121+
$payload = json_decode($body = $response->getBody()->getContents(), true);
122+
123+
$this->assertEquals('custom endpoint 1', $payload['data']['message'], $body);
124+
}
125+
126+
#[Test]
127+
public function custom_endpoint_works_if_added_before_all()
128+
{
129+
$this->extend(
130+
(new Extend\ApiResource(DiscussionResource::class))
131+
->endpointsBeforeAll(fn () => [
132+
Endpoint::make('custom')
133+
->route('GET', '/custom')
134+
->action(function (Context $context) {
135+
return [
136+
'data' => [
137+
'message' => 'custom endpoint',
138+
],
139+
];
140+
}),
141+
])
142+
);
143+
144+
$response = $this->send(
145+
$this->request('GET', '/api/discussions/custom', [
146+
'authenticatedAs' => 1,
147+
])
148+
);
149+
150+
$payload = json_decode($body = $response->getBody()->getContents(), true);
151+
152+
$this->assertEquals('custom endpoint', $payload['data']['message'], $body);
153+
}
154+
94155
#[Test]
95156
public function after_endpoint_callback_works_with_invokable_classes()
96157
{

0 commit comments

Comments
 (0)