Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 57 additions & 23 deletions WebFiori/Http/APITestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ class APITestCase extends TestCase {
*
* @var array
*/
private $backupGlobals;
private $globalsBackup;

protected function setUp(): void {
parent::setUp();
$this->backupGlobals = [
$this->globalsBackup = [
'GET' => $_GET,
'POST' => $_POST,
'FILES' => $_FILES,
Expand All @@ -48,10 +48,11 @@ protected function setUp(): void {
}

protected function tearDown(): void {
$_GET = $this->backupGlobals['GET'];
$_POST = $this->backupGlobals['POST'];
$_FILES = $this->backupGlobals['FILES'];
$_SERVER = $this->backupGlobals['SERVER'];
$_GET = $this->globalsBackup['GET'];
$_POST = $this->globalsBackup['POST'];
$_FILES = $this->globalsBackup['FILES'];
$_SERVER = $this->globalsBackup['SERVER'];
SecurityContext::clear();
parent::tearDown();
}
/**
Expand Down Expand Up @@ -98,7 +99,7 @@ public function addFile(string $fileIdx, string $filePath, bool $reset = false)
$_FILES[$fileIdx]['error'] = [];
}
$info = $this->extractPathAndName($filePath);
$path = $info['path'].DS.$info['name'];
$path = $info['path'].DIRECTORY_SEPARATOR.$info['name'];

$_FILES[$fileIdx]['name'][] = $info['name'];
$_FILES[$fileIdx]['type'][] = mime_content_type($path);
Expand Down Expand Up @@ -126,17 +127,22 @@ public function addFile(string $fileIdx, string $filePath, bool $reset = false)
* @param array $httpHeaders An optional associative array that can be used
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output of the endpoint.
*/
public function callEndpoint(WebServicesManager $manager, string $requestMethod, string $apiEndpointName, array $parameters = [], array $httpHeaders = []) : string {
public function callEndpoint(WebServicesManager $manager, string $requestMethod, string $apiEndpointName, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
$method = strtoupper($requestMethod);
$serviceName = $this->resolveServiceName($apiEndpointName);

$this->setupRequest($method, $serviceName, $parameters, $httpHeaders);

$manager->setOutputStream(fopen($this->getOutputFile(), 'w'));
$manager->setRequest(Request::createFromGlobals());
SecurityContext::setCurrentUser($user);
$manager->process();

$result = $manager->readOutputStream();
Expand Down Expand Up @@ -178,7 +184,11 @@ private function resolveServiceName(string $nameOrClass): string {
* @param string $method HTTP method
* @param string $serviceName Service name
* @param array $parameters Request parameters
* @param array $httpHeaders HTTP headers
* @param array $httpHeaders An optional associative array that can be used
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
*/
private function setupRequest(string $method, string $serviceName, array $parameters, array $httpHeaders) {
putenv('REQUEST_METHOD=' . $method);
Expand Down Expand Up @@ -257,11 +267,15 @@ public function format(string $output) {
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function deleteRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::DELETE, $endpoint, $parameters, $httpHeaders);
public function deleteRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::DELETE, $endpoint, $parameters, $httpHeaders, $user);
}
/**
* Sends a GET request to specific endpoint.
Expand All @@ -276,8 +290,8 @@ public function deleteRequest(WebServicesManager $manager, string $endpoint, arr
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function getRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::GET, $endpoint, $parameters, $httpHeaders);
public function getRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::GET, $endpoint, $parameters, $httpHeaders, $user);
}
/**
* Sends a POST request to specific endpoint.
Expand All @@ -293,11 +307,15 @@ public function getRequest(WebServicesManager $manager, string $endpoint, array
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function postRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::POST, $endpoint, $parameters, $httpHeaders);
public function postRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::POST, $endpoint, $parameters, $httpHeaders, $user);
}
/**
* Sends a PUT request to specific endpoint.
Expand All @@ -313,11 +331,15 @@ public function postRequest(WebServicesManager $manager, string $endpoint, array
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function putRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::PUT, $endpoint, $parameters, $httpHeaders);
public function putRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::PUT, $endpoint, $parameters, $httpHeaders, $user);
}
/**
* Sends a PATCH request to specific endpoint.
Expand All @@ -333,11 +355,15 @@ public function putRequest(WebServicesManager $manager, string $endpoint, array
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function patchRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::PATCH, $endpoint, $parameters, $httpHeaders);
public function patchRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::PATCH, $endpoint, $parameters, $httpHeaders, $user);
}
/**
* Sends an OPTIONS request to specific endpoint.
Expand All @@ -353,11 +379,15 @@ public function patchRequest(WebServicesManager $manager, string $endpoint, arra
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function optionsRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::OPTIONS, $endpoint, $parameters, $httpHeaders);
public function optionsRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::OPTIONS, $endpoint, $parameters, $httpHeaders, $user);
}
/**
* Sends a HEAD request to specific endpoint.
Expand All @@ -373,11 +403,15 @@ public function optionsRequest(WebServicesManager $manager, string $endpoint, ar
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function headRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = []) : string {
return $this->callEndpoint($manager, RequestMethod::HEAD, $endpoint, $parameters, $httpHeaders);
public function headRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::HEAD, $endpoint, $parameters, $httpHeaders, $user);
}
private function extractPathAndName($absPath): array {
$DS = DIRECTORY_SEPARATOR;
Expand Down
8 changes: 8 additions & 0 deletions WebFiori/Http/Annotations/AllowAnonymous.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class AllowAnonymous {
}
8 changes: 8 additions & 0 deletions WebFiori/Http/Annotations/DeleteMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
class DeleteMapping {
}
8 changes: 8 additions & 0 deletions WebFiori/Http/Annotations/GetMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
class GetMapping {
}
8 changes: 8 additions & 0 deletions WebFiori/Http/Annotations/PostMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
class PostMapping {
}
11 changes: 11 additions & 0 deletions WebFiori/Http/Annotations/PreAuthorize.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class PreAuthorize {
public function __construct(
public readonly string $expression
) {}
}
8 changes: 8 additions & 0 deletions WebFiori/Http/Annotations/PutMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
class PutMapping {
}
15 changes: 15 additions & 0 deletions WebFiori/Http/Annotations/RequestParam.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class RequestParam {
public function __construct(
public readonly string $name,
public readonly string $type = 'string',
public readonly bool $optional = false,
public readonly mixed $default = null,
public readonly string $description = ''
) {}
}
8 changes: 8 additions & 0 deletions WebFiori/Http/Annotations/RequiresAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class RequiresAuth {
}
26 changes: 26 additions & 0 deletions WebFiori/Http/Annotations/ResponseBody.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

/**
* Annotation to automatically convert method return values to HTTP responses.
*
* When applied to a method, the framework will automatically process the return value
* and convert it to an appropriate HTTP response with JSON content.
*
* @author Ibrahim
*/
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class ResponseBody {
/**
* Creates a new ResponseBody annotation.
*
* @param int $status The HTTP status code to return (default: 200)
* @param string $type The response type indicator (default: 'success')
*/
public function __construct(
public readonly int $status = 200,
public readonly string $type = 'success'
) {}
}
12 changes: 12 additions & 0 deletions WebFiori/Http/Annotations/RestController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
namespace WebFiori\Http\Annotations;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class RestController {
public function __construct(
public readonly string $name = '',
public readonly string $description = ''
) {}
}
14 changes: 14 additions & 0 deletions WebFiori/Http/Exceptions/BadRequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace WebFiori\Http\Exceptions;

/**
* Exception for 400 Bad Request responses.
*
* @author Ibrahim
*/
class BadRequestException extends HttpException {

public function __construct(string $message = 'Bad Request') {
parent::__construct($message, 400, 'error');
}
}
14 changes: 14 additions & 0 deletions WebFiori/Http/Exceptions/DuplicateMappingException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace WebFiori\Http\Exceptions;

/**
* Exception thrown when duplicate HTTP method mappings are detected.
*
* @author Ibrahim
*/
class DuplicateMappingException extends \Exception {

public function __construct(string $message) {
parent::__construct($message);
}
}
14 changes: 14 additions & 0 deletions WebFiori/Http/Exceptions/ForbiddenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace WebFiori\Http\Exceptions;

/**
* Exception for 403 Forbidden responses.
*
* @author Ibrahim
*/
class ForbiddenException extends HttpException {

public function __construct(string $message = 'Forbidden') {
parent::__construct($message, 403, 'error');
}
}
29 changes: 29 additions & 0 deletions WebFiori/Http/Exceptions/HttpException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
namespace WebFiori\Http\Exceptions;

use Exception;

/**
* Base class for HTTP exceptions that can be automatically converted to HTTP responses.
*
* @author Ibrahim
*/
abstract class HttpException extends Exception {

protected int $statusCode;
protected string $responseType;

public function __construct(string $message = '', int $statusCode = 500, string $responseType = 'error') {
parent::__construct($message);
$this->statusCode = $statusCode;
$this->responseType = $responseType;
}

public function getStatusCode(): int {
return $this->statusCode;
}

public function getResponseType(): string {
return $this->responseType;
}
}
Loading
Loading