Change API to use PSR-7 style requests and responses

This required some interface changes (mostly changing Laravel's or
Symfony's request and response classes to those of Zend's Diactoros.
Some smaller changes to the execution flow in a few of the abstract
action base classes, but nothing substantial.

Note: The request and response classes are immutable, so we usually
need to return new instances after modifying the old ones.
This commit is contained in:
Franz Liedke 2015-05-27 00:29:31 +02:00
parent ff3196db4b
commit 26c9c17a1b
32 changed files with 264 additions and 323 deletions

View File

@ -8,7 +8,7 @@ interface ActionInterface
* Handle a request to the API, returning an HTTP response.
*
* @param \Flarum\Api\Request $request
* @return \Illuminate\Http\Response
* @return \Psr\Http\Message\ResponseInterface
*/
public function handle(Request $request);
}

View File

@ -4,7 +4,7 @@ use Flarum\Core\Repositories\UserRepositoryInterface;
use Flarum\Core\Repositories\ActivityRepositoryInterface;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
@ -61,10 +61,10 @@ class IndexAction extends SerializeCollectionAction
* document response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$actor = $request->actor->getUser();

View File

@ -1,29 +1,39 @@
<?php namespace Flarum\Api\Actions;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Flarum\Api\Request;
use Tobscure\JsonApi\Document;
abstract class CreateAction extends SerializeResourceAction
{
/**
* Delegate creation of the resource, and set a 201 Created status code on
* the response.
* Set a 201 Created status code on the response.
*
* @param \Flarum\Api\Request $request
* @return \Psr\Http\Message\ResponseInterface
*/
public function respond(Request $request)
{
return parent::respond($request)->withStatus(201);
}
/**
* Get the newly created resource to be serialized and assigned to the response document.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Model
* @param \Tobscure\JsonApi\Document $document
* @return array
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$response->setStatusCode(201);
return $this->create($request, $response);
return $this->create($request);
}
/**
* Create the resource.
*
* @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model
*/
abstract protected function create(JsonApiRequest $request, JsonApiResponse $response);
abstract protected function create(JsonApiRequest $request);
}

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions;
use Flarum\Api\Request;
use Illuminate\Http\Response;
use Zend\Diactoros\Response;
abstract class DeleteAction extends JsonApiAction
{
@ -10,21 +10,20 @@ abstract class DeleteAction extends JsonApiAction
* response.
*
* @param \Flarum\Api\Request $request
* @return \Flarum\Api\Response
* @return \Psr\Http\Message\ResponseInterface
*/
public function respond(Request $request)
{
$this->delete($request, $response = new Response('', 204));
$this->delete($request);
return $response;
return new Response('', 204);
}
/**
* Delete the resource.
*
* @param \Flarum\Api\Request $request
* @param \Flarum\Api\Response $response
* @return void
*/
abstract protected function delete(Request $request, Response $response);
abstract protected function delete(Request $request);
}

View File

@ -5,7 +5,6 @@ use Flarum\Core\Commands\ReadDiscussionCommand;
use Flarum\Core\Models\Forum;
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction
@ -60,11 +59,10 @@ class CreateAction extends BaseCreateAction
/**
* Create a discussion according to input from the API request.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Discussion
* @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model
*/
protected function create(JsonApiRequest $request, JsonApiResponse $response)
protected function create(JsonApiRequest $request)
{
$user = $request->actor->getUser();

View File

@ -3,7 +3,6 @@
use Flarum\Core\Commands\DeleteDiscussionCommand;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Illuminate\Http\Response;
use Illuminate\Contracts\Bus\Dispatcher;
class DeleteAction extends BaseDeleteAction
@ -29,10 +28,9 @@ class DeleteAction extends BaseDeleteAction
* Delete a discussion.
*
* @param \Flarum\Api\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
protected function delete(Request $request, Response $response)
protected function delete(Request $request)
{
$this->bus->dispatch(
new DeleteDiscussionCommand($request->get('id'), $request->actor->getUser())

View File

@ -4,7 +4,7 @@ use Flarum\Core\Search\Discussions\DiscussionSearchCriteria;
use Flarum\Core\Search\Discussions\DiscussionSearcher;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
@ -58,10 +58,10 @@ class IndexAction extends SerializeCollectionAction
* document response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$criteria = new DiscussionSearchCriteria(
$request->actor->getUser(),
@ -73,10 +73,15 @@ class IndexAction extends SerializeCollectionAction
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $load);
if (($total = $results->getTotal()) !== null) {
$response->content->addMeta('total', $total);
$document->addMeta('total', $total);
}
static::addPaginationLinks($response, $request, route('flarum.api.discussions.index'), $total ?: $results->areMoreResults());
static::addPaginationLinks(
$document,
$request,
route('flarum.api.discussions.index'),
$total ?: $results->areMoreResults()
);
return $results->getDiscussions();
}

View File

@ -5,7 +5,7 @@ use Flarum\Core\Repositories\PostRepositoryInterface;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\Actions\Posts\GetsPosts;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
@ -84,10 +84,10 @@ class ShowAction extends SerializeResourceAction
* JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Discussion
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$user = $request->actor->getUser();

View File

@ -3,10 +3,9 @@
use Flarum\Core\Commands\EditDiscussionCommand;
use Flarum\Core\Commands\ReadDiscussionCommand;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\Actions\Posts\GetsPosts;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
@ -47,10 +46,10 @@ class UpdateAction extends SerializeResourceAction
* it ready to be serialized and assigned to the JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Discussion
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$user = $request->actor->getUser();
$discussionId = $request->get('id');

View File

@ -3,7 +3,7 @@
use Flarum\Core\Models\Group;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
@ -19,10 +19,10 @@ class IndexAction extends SerializeCollectionAction
* response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return Group::get();
}

View File

@ -1,12 +1,10 @@
<?php namespace Flarum\Api\Actions;
use Closure;
use Flarum\Api\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Exceptions\ValidationFailureException;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Zend\Diactoros\Response;
abstract class JsonApiAction implements ActionInterface
{
@ -15,7 +13,7 @@ abstract class JsonApiAction implements ActionInterface
* (API-related) exceptions that are thrown.
*
* @param \Flarum\Api\Request $request
* @return \Flarum\Api\Response
* @return \Psr\Http\Message\ResponseInterface
*/
public function handle(Request $request)
{
@ -29,19 +27,28 @@ abstract class JsonApiAction implements ActionInterface
'path' => $field
];
}
return new JsonResponse(['errors' => $errors], 422);
return $this->json(['errors' => $errors], 422);
} catch (PermissionDeniedException $e) {
return new Response(null, 401);
return $this->json(null, 401);
} catch (ModelNotFoundException $e) {
return new Response(null, 404);
return $this->json(null, 404);
}
}
protected function json($data, $status = 200)
{
if ($data === null) $data = new \ArrayObject();
$data = json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
return new Response($data, $status);
}
/**
* Handle an API request and return an API response.
*
* @param \Flarum\Api\Request $request
* @return \Flarum\Api\Response
* @return \Psr\Http\Message\ResponseInterface
*/
abstract protected function respond(Request $request);
}

View File

@ -4,7 +4,7 @@ use Flarum\Core\Repositories\NotificationRepositoryInterface;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
@ -60,10 +60,11 @@ class IndexAction extends SerializeCollectionAction
* document response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
* @throws PermissionDeniedException
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
if (! $request->actor->isAuthenticated()) {
throw new PermissionDeniedException;

View File

@ -3,8 +3,8 @@
use Flarum\Core\Commands\ReadNotificationCommand;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
@ -35,10 +35,10 @@ class UpdateAction extends SerializeResourceAction
* assigned to the JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Notification
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new ReadNotificationCommand($request->get('id'), $request->actor->getUser())

View File

@ -4,7 +4,6 @@ use Flarum\Core\Commands\PostReplyCommand;
use Flarum\Core\Commands\ReadDiscussionCommand;
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction
@ -38,11 +37,10 @@ class CreateAction extends BaseCreateAction
/**
* Reply to a discussion according to input from the API request.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Post
* @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model
*/
protected function create(JsonApiRequest $request, JsonApiResponse $response)
protected function create(JsonApiRequest $request)
{
$user = $request->actor->getUser();

View File

@ -3,7 +3,6 @@
use Flarum\Core\Commands\DeletePostCommand;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Illuminate\Http\Response;
use Illuminate\Contracts\Bus\Dispatcher;
class DeleteAction extends BaseDeleteAction
@ -27,10 +26,9 @@ class DeleteAction extends BaseDeleteAction
* Delete a post.
*
* @param \Flarum\Api\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
protected function delete(Request $request, Response $response)
protected function delete(Request $request)
{
$this->bus->dispatch(
new DeletePostCommand($request->get('id'), $request->actor->getUser())

View File

@ -3,7 +3,7 @@
use Flarum\Core\Repositories\PostRepositoryInterface;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
@ -50,10 +50,10 @@ class IndexAction extends SerializeCollectionAction
* document response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$postIds = (array) $request->get('ids');
$user = $request->actor->getUser();

View File

@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Repositories\PostRepositoryInterface;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
@ -49,10 +49,10 @@ class ShowAction extends SerializeResourceAction
* response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Discussion
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return $this->posts->findOrFail($request->get('id'), $request->actor->getUser());
}

View File

@ -3,8 +3,8 @@
use Flarum\Core\Commands\EditPostCommand;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
@ -35,10 +35,10 @@ class UpdateAction extends SerializeResourceAction
* ready to be serialized and assigned to the JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Post
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new EditPostCommand($request->get('id'), $request->actor->getUser(), $request->get('data'))

View File

@ -3,9 +3,9 @@
use Flarum\Api\Request;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\SerializerInterface;
use Tobscure\JsonApi\Criteria;
use Illuminate\Http\Response;
abstract class SerializeAction extends JsonApiAction
{
@ -63,29 +63,30 @@ abstract class SerializeAction extends JsonApiAction
* Handle an API request and return an API response.
*
* @param \Flarum\Api\Request $request
* @return \Flarum\Api\Response
* @return \Psr\Http\Message\ResponseInterface
*/
public function respond(Request $request)
{
$request = static::buildJsonApiRequest($request);
$data = $this->data($request, $response = new JsonApiResponse);
$document = new Document();
$data = $this->data($request, $document);
$serializer = new static::$serializer($request->actor, $request->include, $request->link);
$response->content->setData($this->serialize($serializer, $data));
$document->setData($this->serialize($serializer, $data));
return $response;
return new JsonApiResponse($document);
}
/**
* Get the data to be serialized and assigned to the response document.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return array
*/
abstract protected function data(JsonApiRequest $request, JsonApiResponse $response);
abstract protected function data(JsonApiRequest $request, Document $document);
/**
* Serialize the data as appropriate.
@ -157,7 +158,7 @@ abstract class SerializeAction extends JsonApiAction
* Add pagination links to a JSON-API response, based on input parameters
* and the default parameters of this action.
*
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @param \Flarum\Api\JsonApiRequest $request
* @param string $url The base URL to build pagination links with.
* @param integer|boolean $total The total number of results (used to build
@ -165,28 +166,29 @@ abstract class SerializeAction extends JsonApiAction
* is unknown ('last' link is ommitted).
* @return void
*/
protected static function addPaginationLinks(JsonApiResponse $response, JsonApiRequest $request, $url, $total = true)
protected static function addPaginationLinks(Document $document, JsonApiRequest $request, $url, $total = true)
{
$input = [];
if ($request->limit != static::$limit) {
array_set($input, 'page.limit', $request->limit);
}
array_set($input, 'page.offset', 0);
$response->content->addLink('first', $url.'?'.http_build_query($input));
$document->addLink('first', $url.'?'.http_build_query($input));
if ($request->offset > 0) {
array_set($input, 'page.offset', max(0, $request->offset - $request->limit));
$response->content->addLink('prev', $url.'?'.http_build_query($input));
$document->addLink('prev', $url.'?'.http_build_query($input));
}
if ($total === true || $request->offset + $request->limit < $total) {
array_set($input, 'page.offset', $request->offset + $request->limit);
$response->content->addLink('next', $url.'?'.http_build_query($input));
$document->addLink('next', $url.'?'.http_build_query($input));
}
if ($total && $total !== true) {
array_set($input, 'page.offset', $total - $request->limit);
$response->content->addLink('last', $url.'?'.http_build_query($input));
$document->addLink('last', $url.'?'.http_build_query($input));
}
}
}

View File

@ -4,7 +4,6 @@ use Flarum\Api\Request;
use Flarum\Core\Commands\GenerateAccessTokenCommand;
use Flarum\Core\Repositories\UserRepositoryInterface;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Bus\Dispatcher;
class TokenAction extends JsonApiAction
@ -23,7 +22,8 @@ class TokenAction extends JsonApiAction
* Log in and return a token.
*
* @param \Flarum\Api\Request $request
* @return \Flarum\Api\Response
* @return \Psr\Http\Message\ResponseInterface
* @throws PermissionDeniedException
*/
public function respond(Request $request)
{
@ -40,7 +40,7 @@ class TokenAction extends JsonApiAction
new GenerateAccessTokenCommand($user->id)
);
return new JsonResponse([
return $this->json([
'token' => $token->id,
'userId' => $user->id
]);

View File

@ -4,7 +4,6 @@ use Flarum\Core\Models\Forum;
use Flarum\Core\Commands\RegisterUserCommand;
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction
@ -45,11 +44,10 @@ class CreateAction extends BaseCreateAction
/**
* Register a user according to input from the API request.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\User
* @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model
*/
protected function create(JsonApiRequest $request, JsonApiResponse $response)
protected function create(JsonApiRequest $request)
{
return $this->bus->dispatch(
new RegisterUserCommand($request->actor->getUser(), $this->forum, $request->get('data'))

View File

@ -3,7 +3,6 @@
use Flarum\Core\Commands\DeleteUserCommand;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Illuminate\Http\Response;
use Illuminate\Contracts\Bus\Dispatcher;
class DeleteAction extends BaseDeleteAction
@ -29,10 +28,9 @@ class DeleteAction extends BaseDeleteAction
* Delete a user.
*
* @param \Flarum\Api\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
protected function delete(Request $request, Response $response)
protected function delete(Request $request)
{
$this->bus->dispatch(
new DeleteUserCommand($request->get('id'), $request->actor->getUser())

View File

@ -3,8 +3,8 @@
use Flarum\Core\Commands\DeleteAvatarCommand;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class DeleteAvatarAction extends SerializeResourceAction
{
@ -35,10 +35,10 @@ class DeleteAvatarAction extends SerializeResourceAction
* assigned to the JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\User
* @param \Tobscure\JsonApi\Document $document
* @return \Flarum\Core\Models\Discussion
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new DeleteAvatarCommand($request->get('id'), $request->actor->getUser())

View File

@ -4,21 +4,21 @@ use Flarum\Core\Search\Users\UserSearchCriteria;
use Flarum\Core\Search\Users\UserSearcher;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
/**
* The user searcher.
*
* @var \Flarum\Core\Search\Discussions\UserSearcher
* @var \Flarum\Core\Search\Users\UserSearcher
*/
protected $searcher;
/**
* Instantiate the action.
*
* @param \Flarum\Core\Search\Discussions\UserSearcher $searcher
* @param \Flarum\Core\Search\Users\UserSearcher $searcher
*/
public function __construct(UserSearcher $searcher)
{
@ -54,10 +54,10 @@ class IndexAction extends SerializeCollectionAction
* document response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$criteria = new UserSearchCriteria(
$request->actor->getUser(),
@ -68,10 +68,11 @@ class IndexAction extends SerializeCollectionAction
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $request->include);
if (($total = $results->getTotal()) !== null) {
$response->content->addMeta('total', $total);
$document->addMeta('total', $total);
}
static::addPaginationLinks($response, $request, route('flarum.api.users.index'), $total ?: $results->areMoreResults());
// TODO: Add route() method!
static::addPaginationLinks($document, $request, 'flarum.api.users.index', $total ?: $results->areMoreResults());
return $results->getUsers();
}

View File

@ -3,7 +3,7 @@
use Flarum\Core\Repositories\UserRepositoryInterface;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
@ -44,10 +44,10 @@ class ShowAction extends SerializeResourceAction
* response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @param \Tobscure\JsonApi\Document $document
* @return \Flarum\Core\Models\Discussion
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
$id = $request->get('id');

View File

@ -3,8 +3,8 @@
use Flarum\Core\Commands\EditUserCommand;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
@ -35,10 +35,10 @@ class UpdateAction extends SerializeResourceAction
* ready to be serialized and assigned to the JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\Post
* @param \Tobscure\JsonApi\Document $document
* @return \Flarum\Core\Models\Discussion
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new EditUserCommand($request->get('id'), $request->actor->getUser(), $request->get('data'))

View File

@ -3,8 +3,8 @@
use Flarum\Core\Commands\UploadAvatarCommand;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\JsonApiResponse;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UploadAvatarAction extends SerializeResourceAction
{
@ -35,13 +35,17 @@ class UploadAvatarAction extends SerializeResourceAction
* and assigned to the JsonApi response.
*
* @param \Flarum\Api\JsonApiRequest $request
* @param \Flarum\Api\JsonApiResponse $response
* @return \Flarum\Core\Models\User
* @param \Tobscure\JsonApi\Document $document
* @return \Flarum\Core\Models\Discussion
*/
protected function data(JsonApiRequest $request, JsonApiResponse $response)
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new UploadAvatarCommand($request->get('id'), $request->http->file('avatar'), $request->actor->getUser())
new UploadAvatarCommand(
$request->get('id'),
$request->http->getUploadedFiles()['avatar'],
$request->actor->getUser()
)
);
}
}

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api;
use Flarum\Http\Router;
use Illuminate\Support\ServiceProvider;
use Flarum\Api\Serializers\BaseSerializer;
class ApiServiceProvider extends ServiceProvider
{
@ -17,6 +17,8 @@ class ApiServiceProvider extends ServiceProvider
'Flarum\Api\ExceptionHandler'
);
$this->app->singleton('Flarum\Http\Router', function() { return new Router(); });
include __DIR__.'/routes.php';
}

View File

@ -1,19 +1,14 @@
<?php namespace Flarum\Api;
use Illuminate\Http\Response;
use Tobscure\JsonApi\Document;
use Zend\Diactoros\Response;
class JsonApiResponse extends Response
{
public $content;
public function __construct($data = null, $status = 200, array $headers = [])
public function __construct(Document $document)
{
parent::__construct('', $status, $headers);
parent::__construct('php://memory', 200, ['content-type' => 'application/vnd.api+json']);
$this->headers->set('Content-Type', 'application/vnd.api+json');
$this->content = new Document;
$this->content->setData($data);
$this->getBody()->write($document);
}
}

View File

@ -2,12 +2,20 @@
use Flarum\Core\Models\AccessToken;
use Flarum\Support\Actor;
use Closure;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Stratigility\MiddlewareInterface;
class LoginWithHeader
class LoginWithHeader implements MiddlewareInterface
{
/**
* @var Actor
*/
protected $actor;
/**
* @var string
*/
protected $prefix = 'Token ';
// @todo rather than using a singleton, we should have our own HTTP
@ -17,17 +25,21 @@ class LoginWithHeader
$this->actor = $actor;
}
public function handle($request, Closure $next)
/**
* {@inheritdoc}
*/
public function __invoke(Request $request, Response $response, callable $out = null)
{
$header = $request->headers->get('authorization');
$header = $request->getHeaderLine('authorization');
if (starts_with($header, $this->prefix) &&
($token = substr($header, strlen($this->prefix))) &&
($accessToken = AccessToken::where('id', $token)->first())) {
($accessToken = AccessToken::where('id', $token)->first())
) {
$this->actor->setUser($user = $accessToken->user);
$user->updateLastSeen()->save();
}
return $next($request);
return $out ? $out($request, $response) : $response;
}
}

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api;
use Flarum\Support\Actor;
use Illuminate\Http\Request as IlluminateRequest;
use Psr\Http\Message\ServerRequestInterface;
class Request
{
@ -9,9 +9,12 @@ class Request
public $actor;
/**
* @var ServerRequestInterface
*/
public $http;
public function __construct(array $input, Actor $actor = null, IlluminateRequest $http = null)
public function __construct(array $input, Actor $actor = null, ServerRequestInterface $http = null)
{
$this->input = $input;
$this->actor = $actor;

View File

@ -1,227 +1,140 @@
<?php
use Flarum\Api\Request;
use Flarum\Api\Request as ApiRequest;
use Psr\Http\Message\ServerRequestInterface;
$action = function ($class) {
return function () use ($class) {
return function (ServerRequestInterface $httpRequest, $routeParams) use ($class) {
$action = $this->app->make($class);
$actor = $this->app->make('Flarum\Support\Actor');
$httpRequest = $this->app['request']->instance();
$routeParams = $this->app['router']->current()->parameters();
$actor = $this->app['Flarum\Support\Actor'];
if (str_contains($httpRequest->header('CONTENT_TYPE'), 'application/vnd.api+json')) {
$input = $httpRequest->json();
if (str_contains($httpRequest->getHeaderLine('content-type'), 'application/vnd.api+json')) {
$input = json_decode($httpRequest->getBody(), true);
} else {
$input = $httpRequest->all();
$input = $httpRequest->getAttributes();
}
$input = array_merge($input, $routeParams);
$request = new Request($input, $actor, $httpRequest);
$request = new ApiRequest($input, $actor, $httpRequest);
return $action->handle($request);
};
};
Route::group(['prefix' => 'api', 'middleware' => 'Flarum\Api\Middleware\LoginWithHeader'], function () use ($action) {
/** @var Flarum\Http\Router $router */
$router = $this->app->make('Flarum\Http\Router');
// Get forum information
Route::get('forum', [
'as' => 'flarum.api.forum.show',
'uses' => $action('Flarum\Api\Actions\Forum\ShowAction')
]);
// Get forum information
$router->get('/api/forum', 'flarum.api.forum.show', $action('Flarum\Api\Actions\Forum\ShowAction'));
// Retrieve authentication token
Route::post('token', [
'as' => 'flarum.api.token',
'uses' => $action('Flarum\Api\Actions\TokenAction')
]);
// Retrieve authentication token
$router->post('/api/token', 'flarum.api.token', $action('Flarum\Api\Actions\TokenAction'));
// Send forgot password email
Route::post('forgot', [
'as' => 'flarum.api.forgot',
'uses' => $action('Flarum\Api\Actions\Users\ForgotAction')
]);
// Send forgot password email
$router->post('/forgot', 'flarum.api.forgot', $action('Flarum\Api\Actions\ForgotAction'));
/*
|--------------------------------------------------------------------------
| Users
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Users
|--------------------------------------------------------------------------
*/
// List users
Route::get('users', [
'as' => 'flarum.api.users.index',
'uses' => $action('Flarum\Api\Actions\Users\IndexAction')
]);
// List users
$router->get('/api/users', 'flarum.api.users.index', $action('Flarum\Api\Actions\Users\IndexAction'));
// Register a user
Route::post('users', [
'as' => 'flarum.api.users.create',
'uses' => $action('Flarum\Api\Actions\Users\CreateAction')
]);
// Register a user
$router->post('/api/users', 'flarum.api.users.create', $action('Flarum\Api\Actions\Users\CreateAction'));
// Get a single user
Route::get('users/{id}', [
'as' => 'flarum.api.users.show',
'uses' => $action('Flarum\Api\Actions\Users\ShowAction')
]);
// Get a single user
$router->get('/api/users/{id}', 'flarum.api.users.show', $action('Flarum\Api\Actions\Users\ShowAction'));
// Edit a user
Route::put('users/{id}', [
'as' => 'flarum.api.users.update',
'uses' => $action('Flarum\Api\Actions\Users\UpdateAction')
]);
// Edit a user
$router->put('/api/users/{id}', 'flarum.api.users.update', $action('Flarum\Api\Actions\Users\UpdateAction'));
// Delete a user
Route::delete('users/{id}', [
'as' => 'flarum.api.users.delete',
'uses' => $action('Flarum\Api\Actions\Users\DeleteAction')
]);
// Delete a user
$router->delete('/api/users/{id}', 'flarum.api.users.delete', $action('Flarum\Api\Actions\Users\DeleteAction'));
// Upload avatar
Route::post('users/{id}/avatar', [
'as' => 'flarum.api.users.avatar.upload',
'uses' => $action('Flarum\Api\Actions\Users\UploadAvatarAction')
]);
// Upload avatar
$router->post('/api/users/{id}/avatar', 'flarum.api.users.avatar.upload', $action('Flarum\Api\Actions\Users\UploadAvatarAction'));
// Remove avatar
Route::delete('users/{id}/avatar', [
'as' => 'flarum.api.users.avatar.delete',
'uses' => $action('Flarum\Api\Actions\Users\DeleteAvatarAction')
]);
// Remove avatar
$router->delete('/api/users/{id}/avatar', 'flarum.api.users.avatar.delete', $action('Flarum\Api\Actions\Users\DeleteAvatarAction'));
/*
|--------------------------------------------------------------------------
| Activity
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Activity
|--------------------------------------------------------------------------
*/
// List activity
Route::get('activity', [
'as' => 'flarum.api.activity.index',
'uses' => $action('Flarum\Api\Actions\Activity\IndexAction')
]);
// List activity
$router->get('/api/activity', 'flarum.api.activity.index', $action('Flarum\Api\Actions\Activity\IndexAction'));
// List notifications for the current user
Route::get('notifications', [
'as' => 'flarum.api.notifications.index',
'uses' => $action('Flarum\Api\Actions\Notifications\IndexAction')
]);
// List notifications for the current user
$router->get('/api/notifications', 'flarum.api.notifications.index', $action('Flarum\Api\Actions\Notifications\IndexAction'));
// Mark a single notification as read
Route::put('notifications/{id}', [
'as' => 'flarum.api.notifications.update',
'uses' => $action('Flarum\Api\Actions\Notifications\UpdateAction')
]);
// Mark a single notification as read
$router->put('/api/notifications/{id}', 'flarum.api.notifications.update', $action('Flarum\Api\Actions\Notifications\UpdateAction'));
/*
|--------------------------------------------------------------------------
| Discussions
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Discussions
|--------------------------------------------------------------------------
*/
// List discussions
Route::get('discussions', [
'as' => 'flarum.api.discussions.index',
'uses' => $action('Flarum\Api\Actions\Discussions\IndexAction')
]);
// List discussions
$router->get('/api/discussions', 'flarum.api.discussions.index', $action('Flarum\Api\Actions\Discussions\IndexAction'));
// Create a discussion
Route::post('discussions', [
'as' => 'flarum.api.discussions.create',
'uses' => $action('Flarum\Api\Actions\Discussions\CreateAction')
]);
// Create a discussion
$router->post('/api/discussions', 'flarum.api.discussions.create', $action('Flarum\Api\Actions\Discussions\CreateAction'));
// Show a single discussion
Route::get('discussions/{id}', [
'as' => 'flarum.api.discussions.show',
'uses' => $action('Flarum\Api\Actions\Discussions\ShowAction')
]);
// Show a single discussion
$router->get('/api/discussions/{id}', 'flarum.api.discussions.show', $action('Flarum\Api\Actions\Discussions\ShowAction'));
// Edit a discussion
Route::put('discussions/{id}', [
'as' => 'flarum.api.discussions.update',
'uses' => $action('Flarum\Api\Actions\Discussions\UpdateAction')
]);
// Edit a discussion
$router->put('/api/discussions/{id}', 'flarum.api.discussions.update', $action('Flarum\Api\Actions\Discussions\UpdateAction'));
// Delete a discussion
Route::delete('discussions/{id}', [
'as' => 'flarum.api.discussions.delete',
'uses' => $action('Flarum\Api\Actions\Discussions\DeleteAction')
]);
// Delete a discussion
$router->delete('/api/discussions/{id}', 'flarum.api.discussions.delete', $action('Flarum\Api\Actions\Discussions\DeleteAction'));
/*
|--------------------------------------------------------------------------
| Posts
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Posts
|--------------------------------------------------------------------------
*/
// List posts, usually for a discussion
Route::get('posts', [
'as' => 'flarum.api.posts.index',
'uses' => $action('Flarum\Api\Actions\Posts\IndexAction')
]);
// List posts, usually for a discussion
$router->get('/api/posts', 'flarum.api.posts.index', $action('Flarum\Api\Actions\Posts\IndexAction'));
// Create a post
// @todo consider 'discussions/{id}/links/posts'?
Route::post('posts', [
'as' => 'flarum.api.posts.create',
'uses' => $action('Flarum\Api\Actions\Posts\CreateAction')
]);
// Create a post
// @todo consider 'discussions/{id}/links/posts'?
$router->post('/api/posts', 'flarum.api.posts.create', $action('Flarum\Api\Actions\Posts\CreateAction'));
// Show a single or multiple posts by ID
Route::get('posts/{id}', [
'as' => 'flarum.api.posts.show',
'uses' => $action('Flarum\Api\Actions\Posts\ShowAction')
]);
// Show a single or multiple posts by ID
$router->get('/api/posts/{id}', 'flarum.api.posts.show', $action('Flarum\Api\Actions\Posts\ShowAction'));
// Edit a post
Route::put('posts/{id}', [
'as' => 'flarum.api.posts.update',
'uses' => $action('Flarum\Api\Actions\Posts\UpdateAction')
]);
// Edit a post
$router->put('/api/posts/{id}', 'flarum.api.posts.update', $action('Flarum\Api\Actions\Posts\UpdateAction'));
// Delete a post
Route::delete('posts/{id}', [
'as' => 'flarum.api.posts.delete',
'uses' => $action('Flarum\Api\Actions\Posts\DeleteAction')
]);
// Delete a post
$router->delete('/api/posts/{id}', 'flarum.api.posts.delete', $action('Flarum\Api\Actions\Posts\DeleteAction'));
/*
|--------------------------------------------------------------------------
| Groups
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Groups
|--------------------------------------------------------------------------
*/
// List groups
Route::get('groups', [
'as' => 'flarum.api.groups.index',
'uses' => $action('Flarum\Api\Actions\Groups\IndexAction')
]);
// List groups
$router->get('/api/groups', 'flarum.api.groups.index', $action('Flarum\Api\Actions\Groups\IndexAction'));
// Create a group
Route::post('groups', [
'as' => 'flarum.api.groups.create',
'uses' => $action('Flarum\Api\Actions\Groups\CreateAction')
]);
// Create a group
$router->post('/api/groups', 'flarum.api.groups.create', $action('Flarum\Api\Actions\Groups\CreateAction'));
// Show a single group
Route::get('groups/{id}', [
'as' => 'flarum.api.groups.show',
'uses' => $action('Flarum\Api\Actions\Groups\ShowAction')
]);
// Show a single group
$router->get('/api/groups/{id}', 'flarum.api.groups.show', $action('Flarum\Api\Actions\Groups\ShowAction'));
// Edit a group
Route::put('groups/{id}', [
'as' => 'flarum.api.groups.update',
'uses' => $action('Flarum\Api\Actions\Groups\UpdateAction')
]);
// Edit a group
$router->put('/api/groups/{id}', 'flarum.api.groups.update', $action('Flarum\Api\Actions\Groups\UpdateAction'));
// Delete a group
Route::delete('groups/{id}', [
'as' => 'flarum.api.groups.delete',
'uses' => $action('Flarum\Api\Actions\Groups\DeleteAction')
]);
});
// Delete a group
$router->delete('/api/groups/{id}', 'flarum.api.groups.delete', $action('Flarum\Api\Actions\Groups\DeleteAction'));