diff --git a/app/Controllers/ApiValidator.php b/app/Controllers/ApiValidator.php index 37f22d6..a0f6b86 100644 --- a/app/Controllers/ApiValidator.php +++ b/app/Controllers/ApiValidator.php @@ -17,7 +17,7 @@ public function __invoke(Request $request, RequestHandler $handler): ResponseInt if (!$responseBody->getIsAuthenticated()) { // Short circuit the request by returning a response with status of 401; $responseBody = $responseBody - ->setStatus(401) + ->setStatus(ResponseBody::HTTP_UNAUTHORIZED) ->setMessage('Invalid or missing API Key'); return $responseBody(); } diff --git a/app/Controllers/Authenticate/AuthenticatePostAction.php b/app/Controllers/Authenticate/AuthenticatePostAction.php index 5c839c1..b5b5231 100644 --- a/app/Controllers/Authenticate/AuthenticatePostAction.php +++ b/app/Controllers/Authenticate/AuthenticatePostAction.php @@ -42,7 +42,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac // Is the user not found or is the password not valid? if ($user === null || !password_verify($body['password'], $user->PasswordHash)) { $responseBody = $responseBody - ->setStatus(401) + ->setStatus(ResponseBody::HTTP_UNAUTHORIZED) ->setData(null) ->setMessage('Not authorized'); return $responseBody(); @@ -69,7 +69,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac if (!$user->save()) { // Save failed for some reason, so reject the request. $responseBody = $responseBody - ->setStatus(500) + ->setStatus(ResponseBody::HTTP_INTERNAL_SERVER_ERROR) ->setData(null) ->setMessage('Unable to set new API_KEY'); return $responseBody(); @@ -78,7 +78,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac // Request is valid and authenticated! $responseBody = $responseBody ->setIsAuthenticated() - ->setStatus(200) + ->setStatus(ResponseBody::HTTP_OK) ->setData([ 'apiKey' => $user->API_KEY, 'organization' => $user->Organization diff --git a/app/Controllers/Authenticate/AuthenticatePostValidator.php b/app/Controllers/Authenticate/AuthenticatePostValidator.php index 8ebb155..c2318f3 100644 --- a/app/Controllers/Authenticate/AuthenticatePostValidator.php +++ b/app/Controllers/Authenticate/AuthenticatePostValidator.php @@ -38,7 +38,7 @@ public function __invoke(Request $request, RequestHandler $handler): ResponseInt // If there are any missing required, or invalid data points then we short circuit and return invalid request. if ($responseBody->hasMissingRequiredOrInvalid()) { $responseBody = $responseBody - ->setStatus(400) + ->setStatus(ResponseBody::HTTP_BAD_REQUEST) ->setMessage('Missing or invalid request'); return $responseBody(); } diff --git a/app/Controllers/GetActionBase.php b/app/Controllers/GetActionBase.php index 9385ea6..ebc0a95 100644 --- a/app/Controllers/GetActionBase.php +++ b/app/Controllers/GetActionBase.php @@ -32,13 +32,12 @@ public function __invoke(Request $request, Response $response, array $args): Res // If the record is not found then 404 error, otherwise status is 200. if ($model === null) { $data = null; - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; } else { // Remove any protected fields from the response $data = $model->toArray(); $this->sanitize($data, $model::FIELDS); - - $status = 200; + $status = ResponseBody::HTTP_OK; } // Set the status and data of the ResponseBody diff --git a/app/Controllers/MedHistory/MedHistoryDeleteAction.php b/app/Controllers/MedHistory/MedHistoryDeleteAction.php index 0f43da1..64d2ad9 100644 --- a/app/Controllers/MedHistory/MedHistoryDeleteAction.php +++ b/app/Controllers/MedHistory/MedHistoryDeleteAction.php @@ -40,9 +40,9 @@ public function __invoke(Request $request, Response $response, array $args): Res // Destroy the model given the id. if ($model->destroy($args['id']) === 1) { - $status = 200; + $status = ResponseBody::HTTP_OK; } else { - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; } // Set the status and data of the ResponseBody diff --git a/app/Controllers/Medicine/MedicineDeleteAction.php b/app/Controllers/Medicine/MedicineDeleteAction.php index 1c99a2e..e33451b 100644 --- a/app/Controllers/Medicine/MedicineDeleteAction.php +++ b/app/Controllers/Medicine/MedicineDeleteAction.php @@ -40,9 +40,9 @@ public function __invoke(Request $request, Response $response, array $args): Res // Destroy the model given the id. if ($model->destroy($args['id']) === 1) { - $status = 200; + $status = ResponseBody::HTTP_OK; } else { - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; } // Set the status and data of the ResponseBody diff --git a/app/Controllers/PasswordReset/PasswordResetPostAction.php b/app/Controllers/PasswordReset/PasswordResetPostAction.php index 194c33c..81141ff 100644 --- a/app/Controllers/PasswordReset/PasswordResetPostAction.php +++ b/app/Controllers/PasswordReset/PasswordResetPostAction.php @@ -43,7 +43,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac // Is the user not found or is the old_password not valid or the api_key is invalid? if ($user === null || $user->API_KEY != $body['api_key'] || !password_verify($body['old_password'], $user->PasswordHash)) { $responseBody = $responseBody - ->setStatus(401) + ->setStatus(ResponseBody::HTTP_UNAUTHORIZED) ->setData(null) ->setMessage('Not authorized'); return $responseBody(); @@ -59,7 +59,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac if (!$user->save()) { // Save failed for some reason, so reject the request. $responseBody = $responseBody - ->setStatus(500) + ->setStatus(ResponseBody::HTTP_INTERNAL_SERVER_ERROR) ->setData(null) ->setMessage('Unable to set new password'); return $responseBody(); @@ -68,7 +68,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac // Request is valid and authenticated with new API_KEY $responseBody = $responseBody ->setIsAuthenticated() - ->setStatus(200) + ->setStatus(ResponseBody::HTTP_OK) ->setData(['apiKey' => $user->API_KEY]) ->setMessage('Password reset'); return $responseBody(); diff --git a/app/Controllers/PasswordReset/PasswordResetPostValidator.php b/app/Controllers/PasswordReset/PasswordResetPostValidator.php index ba6a9b2..38332e6 100644 --- a/app/Controllers/PasswordReset/PasswordResetPostValidator.php +++ b/app/Controllers/PasswordReset/PasswordResetPostValidator.php @@ -48,7 +48,7 @@ public function __invoke(Request $request, RequestHandler $handler): ResponseInt // If there are any missing required, or invalid data points then we short circuit and return invalid request. if ($responseBody->hasMissingRequiredOrInvalid()) { $responseBody = $responseBody - ->setStatus(400) + ->setStatus(ResponseBody::HTTP_BAD_REQUEST) ->setMessage('Missing or invalid request'); return $responseBody(); } diff --git a/app/Controllers/Resident/ResidentDeleteAction.php b/app/Controllers/Resident/ResidentDeleteAction.php index 2eceee3..f40c2b1 100644 --- a/app/Controllers/Resident/ResidentDeleteAction.php +++ b/app/Controllers/Resident/ResidentDeleteAction.php @@ -40,9 +40,9 @@ public function __invoke(Request $request, Response $response, array $args): Res // Destroy the model given the id. if ($model->destroy($args['id']) === 1) { - $status = 200; + $status = ResponseBody::HTTP_OK; } else { - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; } // Set the status and data of the ResponseBody diff --git a/app/Controllers/Resident/ResidentPostAction.php b/app/Controllers/Resident/ResidentPostAction.php index 9b4afe3..e58d21e 100644 --- a/app/Controllers/Resident/ResidentPostAction.php +++ b/app/Controllers/Resident/ResidentPostAction.php @@ -3,7 +3,11 @@ namespace Willow\Controllers\Resident; +use Psr\Http\Message\ResponseInterface; +use Slim\Psr7\Request; +use Slim\Psr7\Response; use Willow\Controllers\WriteActionBase; +use Willow\Middleware\ResponseBody; use Willow\Models\Resident; use Willow\Models\ModelBase; @@ -15,4 +19,64 @@ public function __construct(Resident $model) { $this->model = $model; } + + /** + * We override this checking for trashed clients to restore or to prevent duplicates + * @param Request $request + * @param Response $response + * @return ResponseInterface + */ + public function __invoke(Request $request, Response $response): ResponseInterface { + /** @var ResponseBody $responseBody */ + $responseBody = $request->getAttribute('response_body'); + $residentModel = clone $this->model; + $modelColumns = $residentModel::FIELDS; + + // Get the request body + $parsedBody = $responseBody->getParsedRequest(); + + // Get the id/Id from the request + $id = $parsedBody['Id'] ?? null; + + // Are we inserting a new record? + if ($id === null) { + // Force UserScope and look for existing records including trashed records + $residentModel = $residentModel-> + where('UserId', '=', $responseBody->getUserId())-> + where('FirstName', '=', $parsedBody['FirstName'])-> + where('LastName', '=', $parsedBody['LastName'])-> + where('DOB_YEAR', '=', $parsedBody['DOB_YEAR'])-> + where('DOB_MONTH', '=', $parsedBody['DOB_MONTH'])-> + where('DOB_DAY', '=', $parsedBody['DOB_DAY'])-> + withTrashed()-> + first(); + + // Did we get any results? + if ($residentModel !== null) { + // Is the client deactivated (trashed)? + if ($residentModel->trashed()) { + // Undelete the record + if ($residentModel->restore()) { + $data = $residentModel->toArray(); + $this->sanitize($data, $modelColumns); + + // Return the response as the restored record. + $responseBody = $responseBody + ->setData($data) + ->setStatus(ResponseBody::HTTP_OK); + return $responseBody(); + } + } else { + // Prevent inserting duplicate clients + $responseBody = $responseBody + ->setData(null) + ->setStatus(ResponseBody::HTTP_CONFLICT) + ->setMessage('Duplicates not allowed'); + return $responseBody(); + } + } + } + + return parent::__invoke($request, $response); + } } diff --git a/app/Controllers/RestoreActionBase.php b/app/Controllers/RestoreActionBase.php index 9b19a83..d898971 100644 --- a/app/Controllers/RestoreActionBase.php +++ b/app/Controllers/RestoreActionBase.php @@ -41,14 +41,14 @@ public function __invoke(Request $request, Response $response): ResponseInterfac // Was the record successfully restored? Return the record and status of 200, otherwise return status 500; if ($record->restore()) { $data = $record->toArray(); - $status = 200; + $status = ResponseBody::HTTP_OK; } else { $data = null; - $status = 500; + $status = ResponseBody::HTTP_INTERNAL_SERVER_ERROR; $message = 'Unable to restore.'; } } else { - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; $data = null; } diff --git a/app/Controllers/RestoreValidatorBase.php b/app/Controllers/RestoreValidatorBase.php index 3a784e5..a4e8212 100644 --- a/app/Controllers/RestoreValidatorBase.php +++ b/app/Controllers/RestoreValidatorBase.php @@ -29,7 +29,7 @@ public function __invoke(Request $request, RequestHandler $handler): ResponseInt // Is there an invalid or missing parameter in the request? Respond with status 400. if ($responseBody->hasMissingRequiredOrInvalid()) { $responseBody = $responseBody - ->setStatus(400) + ->setStatus(ResponseBody::HTTP_BAD_REQUEST) ->setData(null); return $responseBody(); } diff --git a/app/Controllers/SearchActionBase.php b/app/Controllers/SearchActionBase.php index ee10d56..cf52729 100644 --- a/app/Controllers/SearchActionBase.php +++ b/app/Controllers/SearchActionBase.php @@ -95,10 +95,10 @@ public function __invoke(Request $request, Response $response): ResponseInterfac $this->sanitize($datum, $modelColumns); } - $status = 200; + $status = ResponseBody::HTTP_OK; } else { $data = null; - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; } $responseBody = $responseBody diff --git a/app/Controllers/SearchValidatorBase.php b/app/Controllers/SearchValidatorBase.php index 831352a..6726377 100644 --- a/app/Controllers/SearchValidatorBase.php +++ b/app/Controllers/SearchValidatorBase.php @@ -36,7 +36,8 @@ class SearchValidatorBase /** * @param Request $request - * @param RequestHandler $handler + * @param RequestHandler $handlernamespace Willow\Controllers; + * @return ResponseInterface */ public function __invoke(Request $request, RequestHandler $handler): ResponseInterface @@ -100,7 +101,7 @@ public function __invoke(Request $request, RequestHandler $handler): ResponseInt if ($responseBody->hasMissingRequiredOrInvalid()) { $responseBody = $responseBody ->setData(null) - ->setStatus(400); + ->setStatus(ResponseBody::HTTP_BAD_REQUEST); return $responseBody(); } diff --git a/app/Controllers/WriteActionBase.php b/app/Controllers/WriteActionBase.php index aa4978b..aa47f74 100644 --- a/app/Controllers/WriteActionBase.php +++ b/app/Controllers/WriteActionBase.php @@ -31,7 +31,7 @@ public function __invoke(Request $request, Response $response): ResponseInterfac if ($model === null) { $responseBody = $responseBody ->setData(null) - ->setStatus(404); + ->setStatus(ResponseBody::HTTP_NOT_FOUND); return $responseBody(); } } @@ -71,12 +71,12 @@ public function __invoke(Request $request, Response $response): ResponseInterfac $responseBody = $responseBody ->setData($modelArray) - ->setStatus(200); + ->setStatus(ResponseBody::HTTP_OK); } else { // Unable to save for some reason so we return error status. $responseBody = $responseBody ->setData(null) - ->setStatus(500) + ->setStatus(ResponseBody::HTTP_INTERNAL_SERVER_ERROR) ->setMessage('Unable to save changes to ' . $model->getTableName()); } diff --git a/app/Controllers/WriteValidatorBase.php b/app/Controllers/WriteValidatorBase.php index 3e9af62..9c5e405 100644 --- a/app/Controllers/WriteValidatorBase.php +++ b/app/Controllers/WriteValidatorBase.php @@ -21,7 +21,7 @@ public function __invoke(Request $request, RequestHandler $handler): ResponseInt // If there are any missing or required data points then we short circuit and return invalid request. if ($responseBody->hasMissingRequiredOrInvalid()) { $responseBody = $responseBody - ->setStatus(400) + ->setStatus(ResponseBody::HTTP_BAD_REQUEST) ->setMessage('Missing or invalid request'); return $responseBody(); } diff --git a/app/Middleware/JsonBodyParser.php b/app/Middleware/JsonBodyParser.php index dec9e74..49364b2 100644 --- a/app/Middleware/JsonBodyParser.php +++ b/app/Middleware/JsonBodyParser.php @@ -30,7 +30,9 @@ public function process(Request $request, RequestHandler $handler): Response } else { // Short circuit the request by returning a response with status of 400 (invalid request). $responseBody = new ResponseBody(); - $responseBody = $responseBody->setStatus(400)->setMessage('Invalid JSON'); + $responseBody = $responseBody-> + setStatus(ResponseBody::HTTP_BAD_REQUEST)-> + setMessage('Invalid JSON'); return $responseBody(); } } else { @@ -40,7 +42,9 @@ public function process(Request $request, RequestHandler $handler): Response if ($method === 'POST' || $method === 'PATCH') { // Short circuit the request by returning a response with status of 400 (invalid request). $responseBody = new ResponseBody(); - $responseBody = $responseBody->setStatus(400)->setMessage("Invalid Content-Type: $contentType"); + $responseBody = $responseBody-> + setStatus(ResponseBody::HTTP_BAD_REQUEST)-> + setMessage("Invalid Content-Type: $contentType"); return $responseBody(); } } diff --git a/app/Middleware/ResponseBody.php b/app/Middleware/ResponseBody.php index ecd0c7a..74a4b4a 100644 --- a/app/Middleware/ResponseBody.php +++ b/app/Middleware/ResponseBody.php @@ -6,7 +6,7 @@ use Psr\Http\Message\ResponseInterface; use Slim\Psr7\Response; -class ResponseBody +class ResponseBody extends ResponseCodes { protected ?array $parsedRequest = null; protected bool $isAuthenticated = false; @@ -132,7 +132,7 @@ public function getUserId(): int { return $this->userId; } - /** + /** trashed records * Returns true if there are missing or required datapoints in the request * * @return bool diff --git a/app/Middleware/ResponseCodes.php b/app/Middleware/ResponseCodes.php new file mode 100644 index 0000000..e2b4e50 --- /dev/null +++ b/app/Middleware/ResponseCodes.php @@ -0,0 +1,76 @@ + + * @see https://github.com/symfony/http-foundation/blob/eff3fa9a7983f782b93b89322c06e6273fbc4a43/Response.php + */ + public const HTTP_CONTINUE = 100; + public const HTTP_SWITCHING_PROTOCOLS = 101; + public const HTTP_PROCESSING = 102; // RFC2518 + public const HTTP_EARLY_HINTS = 103; // RFC8297 + public const HTTP_OK = 200; + public const HTTP_CREATED = 201; + public const HTTP_ACCEPTED = 202; + public const HTTP_NON_AUTHORITATIVE_INFORMATION = 203; + public const HTTP_NO_CONTENT = 204; + public const HTTP_RESET_CONTENT = 205; + public const HTTP_PARTIAL_CONTENT = 206; + public const HTTP_MULTI_STATUS = 207; // RFC4918 + public const HTTP_ALREADY_REPORTED = 208; // RFC5842 + public const HTTP_IM_USED = 226; // RFC3229 + public const HTTP_MULTIPLE_CHOICES = 300; + public const HTTP_MOVED_PERMANENTLY = 301; + public const HTTP_FOUND = 302; + public const HTTP_SEE_OTHER = 303; + public const HTTP_NOT_MODIFIED = 304; + public const HTTP_USE_PROXY = 305; + public const HTTP_RESERVED = 306; + public const HTTP_TEMPORARY_REDIRECT = 307; + public const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238 + public const HTTP_BAD_REQUEST = 400; + public const HTTP_UNAUTHORIZED = 401; + public const HTTP_PAYMENT_REQUIRED = 402; + public const HTTP_FORBIDDEN = 403; + public const HTTP_NOT_FOUND = 404; + public const HTTP_METHOD_NOT_ALLOWED = 405; + public const HTTP_NOT_ACCEPTABLE = 406; + public const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; + public const HTTP_REQUEST_TIMEOUT = 408; + public const HTTP_CONFLICT = 409; + public const HTTP_GONE = 410; + public const HTTP_LENGTH_REQUIRED = 411; + public const HTTP_PRECONDITION_FAILED = 412; + public const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; + public const HTTP_REQUEST_URI_TOO_LONG = 414; + public const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; + public const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + public const HTTP_EXPECTATION_FAILED = 417; + public const HTTP_I_AM_A_TEAPOT = 418; // RFC2324 + public const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540 + public const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918 + public const HTTP_LOCKED = 423; // RFC4918 + public const HTTP_FAILED_DEPENDENCY = 424; // RFC4918 + public const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04 + public const HTTP_UPGRADE_REQUIRED = 426; // RFC2817 + public const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585 + public const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585 + public const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585 + public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; + public const HTTP_INTERNAL_SERVER_ERROR = 500; + public const HTTP_NOT_IMPLEMENTED = 501; + public const HTTP_BAD_GATEWAY = 502; + public const HTTP_SERVICE_UNAVAILABLE = 503; + public const HTTP_GATEWAY_TIMEOUT = 504; + public const HTTP_VERSION_NOT_SUPPORTED = 505; + public const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295 + public const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918 + public const HTTP_LOOP_DETECTED = 508; // RFC5842 + public const HTTP_NOT_EXTENDED = 510; // RFC2774 + public const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585 +} diff --git a/app/Robo/Plugin/Commands/Templates/DeleteActionTemplate.php b/app/Robo/Plugin/Commands/Templates/DeleteActionTemplate.php index 75edb82..457f3dc 100644 --- a/app/Robo/Plugin/Commands/Templates/DeleteActionTemplate.php +++ b/app/Robo/Plugin/Commands/Templates/DeleteActionTemplate.php @@ -45,9 +45,9 @@ public function __invoke(Request $request, Response $response, array $args): Res // Destroy the model given the id. if ($model->destroy($args['id']) === 1) { - $status = 200; + $status = ResponseBody::HTTP_OK; } else { - $status = 404; + $status = ResponseBody::HTTP_NOT_FOUND; } // Set the status and data of the ResponseBody