Skip to content

Commit

Permalink
Merge pull request #110 from creme332/api
Browse files Browse the repository at this point in the history
Setup project for API
  • Loading branch information
creme332 authored Apr 20, 2024
2 parents f9045be + 0bde4b0 commit 4ed8ac9
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 39 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"Steamy\\": "src/",
"Steamy\\Core\\": "src/core/",
"Steamy\\Controller\\": "src/controllers/",
"Steamy\\Controller\\API\\": "src/controllers/api/",
"Steamy\\Model\\": "src/models/"
}
},
Expand Down
11 changes: 5 additions & 6 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,16 @@ There are two types of endpoints:
1. **Public endpoints** : They return a public resource that can be accessed **without a token**.
2. **Protected endpoints** : They return a protected resource that can only be accessed **with a valid JWT token**.

### Authentication
### Session

| Endpoint | Description | Protected |
|---------------------|---------------------------------------------|-----------|
| `GET /api/v1/login` | Authenticates user and returns a JWT token. | No |
| Endpoint | Description | Protected |
|-------------------------|--------------------------------------------------|-----------|
| `POST /api/v1/sessions` | Authenticates admin and creates a session token. | No |

Note:

- Only administrators can receive a JWT token.
- Only administrators can receive a session token.
- Only administrators can access protected endpoints.
- The JWT token expires after 24 hours and a new one must be requested.

### User

Expand Down
3 changes: 3 additions & 0 deletions docs/INSTALLATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,6 @@ In the root directory of the project, run:
```bash
npm install
```
## Autoload setup

Whenever changes are made to the autoload settings in `composer.json`, you must run `composer dump-autoload`.
2 changes: 1 addition & 1 deletion docs/USAGE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ To export database with `mysqldump`:

```bash
mysqldump -u root -p cafe > resources/database/dump/cafe.sql
```
```
3 changes: 2 additions & 1 deletion public/robots.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
User-agent: *
Allow: /
Allow: /
Disallow: /api/
54 changes: 54 additions & 0 deletions src/controllers/API.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Steamy\Controller;

use Steamy\Core\Controller;
use Steamy\Core\Utility;

/**
* Router for API. It is called for URLs of the form `/api/v1/...`
*
* E.g., http://localhost/steamy-sips/public/api/products
*/
class API
{
use Controller;

private string $resource;

public function __construct()
{
header("Content-Type:application/json");

$this->resource = Utility::splitURL()[2] ?? "";
}

/**
* Checks if root relative url starts with /api/v1
* @return bool
*/
private function validateURLFormat(): bool
{
return preg_match("/^api\/v1/", $_GET["url"]) > 0;
}

public function index(): void
{
if (!$this->validateURLFormat()) {
echo "Invalid API URL: " . $_GET["url"];
die();
}

// call appropriate controller to handle resource
$controllerClassName = 'Steamy\\Controller\\API\\' . ucfirst($this->resource);

if (class_exists($controllerClassName)) {
(new $controllerClassName())->index();
} else {
echo "Invalid API resource: " . $this->resource;
die();
}
}
}
57 changes: 57 additions & 0 deletions src/controllers/api/Products.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Steamy\Controller\API;

use Steamy\Model\Product;

/**
* Handles /products route of api
*/
class Products
{
private function getProducts(): void
{
$all_products = Product::getAll();
$result = [];
foreach ($all_products as $product) {
$result[] = $product->toArray();
}
echo json_encode($result);
}

private function addProduct(): void
{
}

private function deleteProduct(): void
{
}

private function updateProduct(): void
{
}


public function index(): void
{
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
$this->getProducts();
break;
case 'POST':
$this->addProduct();
break;
case 'DELETE':
$this->deleteProduct();
break;
case 'PUT':
$this->updateProduct();
break;
default:
http_response_code(400);
die();
}
}
}
54 changes: 54 additions & 0 deletions src/controllers/api/Sessions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Steamy\Controller\API;

use Steamy\Model\Administrator;

/**
* Handles /sessions route of API
*/
class Sessions
{
private function handleLogin(): void
{
$email = trim($_POST['email'] ?? "");
$password = trim($_POST['password'] ?? "");

if (empty($email) || empty($password)) {
http_response_code(400);
die();
}

// fetch administrator account
$admin = Administrator::getByEmail($email);

// validate email
if (!$admin) {
http_response_code(401);
die();
}

// validate password
if (!$admin->verifyPassword($password)) {
http_response_code(401);
die();
}

$_SESSION['admin_email'] = $email;
session_regenerate_id();
}

public function index(): void
{
switch ($_SERVER['REQUEST_METHOD']) {
case 'POST':
$this->handleLogin();
break;
default:
http_response_code(400);
die();
}
}
}
25 changes: 15 additions & 10 deletions src/core/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Steamy\Core;

use Steamy\Controller\_404;
use Steamy\Controller\API;

class App
{
Expand All @@ -16,16 +17,20 @@ public function loadController(): void
{
$URL = Utility::splitURL();

$controllerClassName = 'Steamy\\Controller\\' . ucfirst($URL[0]);


if (class_exists($controllerClassName)) {
$controller = new $controllerClassName();
} else {
// Fallback to 404 controller
$controller = new _404();
switch ($URL[0]) {
case 'api':
(new API())->index();
break;
default:
$controllerClassName = 'Steamy\\Controller\\' . ucfirst($URL[0]);

if (class_exists($controllerClassName)) {
// call appropriate controller
(new $controllerClassName())->index();
} else {
// Fallback to 404 controller
(new _404())->index();
}
}

$controller->index(); // display contents
}
}
35 changes: 18 additions & 17 deletions src/core/Utility.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@

namespace Steamy\Core;

/**
* Utility class containing various helper functions.
*/
class Utility
{
/**
* Display any data in a formatted block. Use this function
* Displays data in a formatted block. Use this function
* for debugging.
* @param $stuff
* @param $stuff mixed some data
* @return void
*/
public static function show($stuff): void
public static function show(mixed $stuff): void
{
echo "<pre>";
print_r($stuff);
Expand All @@ -25,7 +28,7 @@ public static function show($stuff): void
* This function retrieves the 'url' parameter from the $_GET array or defaults to 'home',
* trims leading and trailing slashes, and then explodes the URL into an array of segments.
*
* @return array An array containing the URL segments.
* @return string[] An array containing the URL segments where each segment is in lowercase.
*/
public static function splitURL(): array
{
Expand All @@ -35,28 +38,27 @@ public static function splitURL(): array

/** @noinspection PhpNoReturnAttributeCanBeAddedInspection */
/**
* Redirects website to a page.
* @param $path string relative URL of page
* Redirects user to a page and ends execution of script.
* - `redirect('home')` redirects to `ROOT`.
* - `redirect('shop/products/1')` redirects to `ROOT/shop/products/1`.
*
* @param $relative_url string root-relative URL of page. It must not start with /.
* @return void
*/
public static function redirect(string $path): void
public static function redirect(string $relative_url): void
{
header("Location: " . ROOT . "/" . $path);
header("Location: " . ROOT . "/" . $relative_url);
die();
}



//Function fuzzySearch will be used to search for products on the shop page.

/**
* Perform fuzzy search on an array of strings.
*
* This function takes a search term and an array of strings, and returns
* an array of strings from the input array that closely match the search term.
* It uses the Levenshtein distance algorithm to determine the similarity between
* the search term and each string in the array.
*
*
* @param string $searchTerm The term to search for.
* @param array $strings The array of strings to search within.
* @param int $threshold The maximum allowed Levenshtein distance.
Expand All @@ -75,13 +77,13 @@ public static function fuzzySearch(string $searchTerm, array $strings, int $thre
}


/**
/**
* Calculates the Levenshtein distance between two strings.
*
* The Levenshtein distance is a metric to measure the difference between two strings.
* It is the minimum number of single-character edits (insertions, deletions, or replaces)
* required to change one word into the other.
*
*
* @param string $str1 The first string.
* @param string $str2 The second string.
* @return int The Levenshtein distance between the two strings.
Expand All @@ -99,7 +101,7 @@ public static function levenshteinDistance(string $str1, string $str2): int
$dp[$i][$j] = 0;
}
}

// Fill the first row and column of the array
for ($i = 0; $i <= $m; $i++) {
$dp[$i][0] = $i;
Expand All @@ -123,5 +125,4 @@ public static function levenshteinDistance(string $str1, string $str2): int
// Return the final result, which is the distance between the two strings
return $dp[$m][$n];
}

}
Loading

0 comments on commit 4ed8ac9

Please sign in to comment.