feat: add CORS handling

This commit is contained in:
Miguel Nogueira 2025-04-16 18:51:53 +01:00
parent 646655bc50
commit 18cfc7adba
3 changed files with 46 additions and 17 deletions

View File

@ -4,19 +4,47 @@ use Controllers\TaskController;
use DI\Bridge\Slim\Bridge; use DI\Bridge\Slim\Bridge;
use DI\Container; use DI\Container;
use Controllers\HomeFrontController; use Controllers\HomeFrontController;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Handlers\Strategies\RequestResponseArgs; use Slim\Handlers\Strategies\RequestResponseArgs;
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
$container = new Container(); $container = new Container();
$app = Bridge::create($container); $app = Bridge::create($container);
$app->addBodyParsingMiddleware(); $app->addBodyParsingMiddleware();
$app->addRoutingMiddleware();
$app->addErrorMiddleware(true, true, true);
// this strategy is preferable because we aren't using a lot of named placeholders // this strategy is preferable because we aren't using a lot of named placeholders
$routeCollector = $app->getRouteCollector(); $routeCollector = $app->getRouteCollector();
$routeCollector->setDefaultInvocationStrategy(new RequestResponseArgs()); $routeCollector->setDefaultInvocationStrategy(new RequestResponseArgs());
$app->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler) use ($app): ResponseInterface {
if ($request->getMethod() === 'OPTIONS') {
$response = $app->getResponseFactory()->createResponse();
} else {
$response = $handler->handle($request);
}
$response = $response
->withHeader('Access-Control-Allow-Credentials', 'true')
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Headers', '*')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
->withHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->withHeader('Pragma', 'no-cache');
if (ob_get_contents()) {
ob_clean();
}
return $response;
});
$app->get('/', [HomeFrontController::class, 'home']); $app->get('/', [HomeFrontController::class, 'home']);

View File

@ -16,7 +16,8 @@ use Utils\StructuredResponseBuilder;
class TaskController class TaskController
{ {
private $repository, $builder; private StructuredResponseBuilder $builder;
private TaskRepository $repository;
// instead of requesting the builder here, we could simply make it static // instead of requesting the builder here, we could simply make it static
public function __construct(TaskRepository $taskRepository, StructuredResponseBuilder $builder) public function __construct(TaskRepository $taskRepository, StructuredResponseBuilder $builder)
@ -34,7 +35,7 @@ class TaskController
$requiredKeys = ['name', 'description', 'status', 'start', 'end']; $requiredKeys = ['name', 'description', 'status', 'start', 'end'];
if (!array_key_exists('tasks', $params)) { if (!array_key_exists('payload', $params)) {
$this->builder->setError(); $this->builder->setError();
$this->builder->setErrorMessage('Malformed request payload. Please try again.'); $this->builder->setErrorMessage('Malformed request payload. Please try again.');
$this->builder->setErrorCode(400); $this->builder->setErrorCode(400);
@ -48,7 +49,7 @@ class TaskController
$missingKeys = []; $missingKeys = [];
foreach ($requiredKeys as $key) foreach ($requiredKeys as $key)
{ {
if (!array_key_exists($key, $params['tasks'])) { if (!array_key_exists($key, $params['payload'])) {
$missingKeys[] = $key; $missingKeys[] = $key;
} }
} }
@ -66,8 +67,8 @@ class TaskController
try try
{ {
$start = Carbon::parse($params['tasks']['start']); $start = Carbon::parse($params['payload']['start']);
$end = Carbon::parse($params['tasks']['end']); $end = Carbon::parse($params['payload']['end']);
if ($start->gt($end) || $end->lt($start)) { if ($start->gt($end) || $end->lt($start)) {
throw new InvalidTaskDateException('Invalid task date. Start date must not be after end date.'); throw new InvalidTaskDateException('Invalid task date. Start date must not be after end date.');
@ -85,7 +86,7 @@ class TaskController
return $errorResponse; return $errorResponse;
} }
if (is_null(TaskStatus::tryFrom($params['tasks']['status']))) { if (is_null(TaskStatus::tryFrom($params['payload']['status']))) {
$this->builder->setError(); $this->builder->setError();
$this->builder->setErrorCode(400); $this->builder->setErrorCode(400);
$this->builder->setErrorMessage('Invalid task status code. Status must range from 1-4 (started, in progress, blocked, completed).'); $this->builder->setErrorMessage('Invalid task status code. Status must range from 1-4 (started, in progress, blocked, completed).');
@ -97,11 +98,11 @@ class TaskController
} }
$task = new Task(); $task = new Task();
$task->setName($params['tasks']['name']) $task->setName($params['payload']['name'])
->setStatusId(TaskStatus::tryFrom((int) $params['tasks']['status'])) ->setStatusId(TaskStatus::tryFrom((int) $params['payload']['status']))
->setDescription($params['tasks']['description']) ->setDescription($params['payload']['description'])
->setStart($params['tasks']['start']) ->setStart($params['payload']['start'])
->setEnd($params['tasks']['end']); ->setEnd($params['payload']['end']);
$createdTask = $this->repository->create($task); $createdTask = $this->repository->create($task);
@ -133,7 +134,7 @@ class TaskController
$this->builder->setPayload($payload)->setOptionalMessage('Listing all tasks: '); $this->builder->setPayload($payload)->setOptionalMessage('Listing all tasks: ');
$response->getBody()->write($this->builder->build()); $response->getBody()->write($this->builder->build());
return $response; return $response->withHeader('Content-Type', 'application/json');
} }
public function getTask(ServerRequestInterface $request, ResponseInterface $response, string $id): ResponseInterface public function getTask(ServerRequestInterface $request, ResponseInterface $response, string $id): ResponseInterface
@ -144,12 +145,12 @@ class TaskController
$this->builder->setPayload($payload)->setOptionalMessage("Task retrieved."); $this->builder->setPayload($payload)->setOptionalMessage("Task retrieved.");
$response->getBody()->write($this->builder->build()); $response->getBody()->write($this->builder->build());
return $response; return $response->withHeader('Content-Type', 'application/json');
} catch (TaskNotFoundException $e) { } catch (TaskNotFoundException $e) {
$this->builder->setError()->setErrorMessage($e->getMessage())->setErrorCode(404); $this->builder->setError()->setErrorMessage($e->getMessage())->setErrorCode(404);
$errorResponse = $response->withStatus(404, $e->getMessage()); $errorResponse = $response->withStatus(404, $e->getMessage())->withHeader('Content-Type', 'application/json');
$response->getBody()->write($this->builder->build()); $response->getBody()->write($this->builder->build());
return $errorResponse; return $errorResponse;
@ -166,7 +167,7 @@ class TaskController
$this->builder->setOptionalMessage('Task deleted.'); $this->builder->setOptionalMessage('Task deleted.');
$response->getBody()->write($this->builder->build()); $response->getBody()->write($this->builder->build());
return $response; return $response->withHeader('Content-Type', 'application/json');
} catch (TaskNotFoundException $exception) } catch (TaskNotFoundException $exception)
{ {
@ -175,7 +176,7 @@ class TaskController
$errorResponse = $response->withStatus(404); $errorResponse = $response->withStatus(404);
$errorResponse->getBody()->write($this->builder->build()); $errorResponse->getBody()->write($this->builder->build());
return $errorResponse; return $errorResponse->withHeader('Content-Type', 'application/json');
} }
} }

View File

@ -37,7 +37,7 @@ class Task
public function __construct($status = TaskStatus::STARTED) public function __construct($status = TaskStatus::STARTED)
{ {
$this->statusId = $status; $this->statusId = $status;
$this->createdAt = Carbon::now();
} }