Skip to content

Commit

Permalink
Negotiation: add v2
Browse files Browse the repository at this point in the history
  • Loading branch information
f3l1x committed May 29, 2024
1 parent cb585f5 commit 86bfb38
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
1 change: 1 addition & 0 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ framex:
tracy: Contributte\FrameX\Middleware\TracyMiddleware(%debugMode%)
cors: Contributte\FrameX\Middleware\CorsMiddleware
negotiation: Contributte\FrameX\Middleware\NegotiationMiddleware
# negotiationv2: Contributte\FrameX\Middleware\NegotiationV2Middleware
# List of routes
routing:
Expand Down
128 changes: 128 additions & 0 deletions src/Middleware/NegotiationV2Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php declare(strict_types = 1);

namespace Contributte\FrameX\Middleware;

use Contributte\FrameX\Debug\TracyDebugger;
use Contributte\FrameX\Exception\LogicalException;
use Contributte\FrameX\Http\EntityListResponse;
use Contributte\FrameX\Http\ErrorResponse;
use Contributte\FrameX\Http\IResponse;
use Contributte\FrameX\Http\PureResponse;
use Nette\Utils\Json;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\Io\BufferedBody;
use React\Http\Message\Response;
use Throwable;

class NegotiationV2Middleware
{

public function __construct(
private bool $catchExceptions = true,
)
{
}

private function handleResponse(ServerRequestInterface $request, IResponse $apiResponse): ResponseInterface
{
$response = new Response();
$response = $response->withStatus($apiResponse->getStatusCode());
$response = $response->withHeader('content-type', 'application/json');

foreach ($apiResponse->getHeaders() as $key => $value) {
$response = $response->withHeader($key, $value);
}

if ($apiResponse instanceof PureResponse) {
return $response->withBody(
new BufferedBody($apiResponse->getPayload())
);
}

if ($apiResponse instanceof EntityListResponse) {
$output = [
'status' => 'ok',
'data' => $apiResponse->getEntities(),
];

$meta = $apiResponse->getMeta();

if ($meta !== []) {
$output['meta'] = $meta;
}

return $response->withBody(
new BufferedBody(
Json::encode($output)
)
);
}

if ($apiResponse instanceof ErrorResponse) {
return $response->withBody(
new BufferedBody(
Json::encode([
'status' => 'error',
'data' => $apiResponse->getPayload(),
])
)
);
}

return $response->withBody(
new BufferedBody(
Json::encode([
'status' => 'ok',
'data' => $apiResponse->getPayload(),
])
)
);
}

private function handleError(ServerRequestInterface $request, Throwable $e): ResponseInterface
{
$response = new Response();
$response = $response->withStatus(Response::STATUS_INTERNAL_SERVER_ERROR);
$response = $response->withHeader('content-type', 'application/json');

$response = $response->withBody(
new BufferedBody(
Json::encode([
'status' => 'error',
'error' => [
'code' => Response::STATUS_INTERNAL_SERVER_ERROR,
'message' => $e->getMessage() !== '' ? $e->getMessage() : 'Internal server error',
],
])
)
);

return $response;
}

public function __invoke(ServerRequestInterface $request, callable $next): ResponseInterface
{
try {
$apiResponse = $next($request);

// If middleware returns PSR-7 response, do not negotiate
if ($apiResponse instanceof ResponseInterface) {
return $apiResponse;
}

if (!($apiResponse instanceof IResponse)) {
throw new LogicalException(sprintf('Response from controller/middleware must be instanceof "%s"', IResponse::class));
}

return $this->handleResponse($request, $apiResponse);
} catch (Throwable $e) {
if (!$this->catchExceptions) {
TracyDebugger::catch($e);
}

return $this->handleError($request, $e);
}
}

}

0 comments on commit 86bfb38

Please sign in to comment.