mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 06:56:05 +00:00
refactor route processing
This commit is contained in:
@@ -63,6 +63,11 @@ interface Request
|
||||
*/
|
||||
public function getRouteParam(string $name) : ?string;
|
||||
|
||||
/**
|
||||
* Get all route parameters.
|
||||
*/
|
||||
public function getRouteParams() : array;
|
||||
|
||||
/**
|
||||
* Get a header value.
|
||||
*/
|
||||
|
||||
@@ -55,10 +55,6 @@ class RequestWrapper implements ApiRequest
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->basePath = $basePath;
|
||||
|
||||
unset($routeParams['controller']);
|
||||
unset($routeParams['actioon']);
|
||||
|
||||
$this->routeParams = $routeParams;
|
||||
}
|
||||
|
||||
@@ -95,6 +91,11 @@ class RequestWrapper implements ApiRequest
|
||||
return $this->routeParams[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getRouteParams() : array
|
||||
{
|
||||
return $this->routeParams;
|
||||
}
|
||||
|
||||
public function hasQueryParam(string $name) : bool
|
||||
{
|
||||
return array_key_exists($name, $this->request->getQueryParams());
|
||||
|
||||
@@ -35,6 +35,7 @@ use Espo\Core\{
|
||||
Utils\Config,
|
||||
Utils\Json,
|
||||
ControllerManager,
|
||||
Exceptions\Error,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
@@ -53,33 +54,11 @@ class RouteProcessor
|
||||
$this->controllerManager = $controllerManager;
|
||||
}
|
||||
|
||||
public function process(string $route, array $routeParams, RequestWrapper $request, ResponseWrapper $response, array $args)
|
||||
public function process(string $route, RequestWrapper $request, ResponseWrapper $response)
|
||||
{
|
||||
$response->setHeader('Content-Type', 'application/json');
|
||||
|
||||
$params = [];
|
||||
|
||||
$paramKeys = array_keys($routeParams);
|
||||
|
||||
$setKeyList = [];
|
||||
|
||||
foreach ($paramKeys as $key) {
|
||||
$value = $routeParams[$key];
|
||||
|
||||
$paramName = $key;
|
||||
if ($value[0] === ':') {
|
||||
$realKey = substr($value, 1);
|
||||
$params[$paramName] = $args[$realKey];
|
||||
$setKeyList[] = $realKey;
|
||||
} else {
|
||||
$params[$paramName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($args as $key => $value) {
|
||||
if (in_array($key, $setKeyList)) continue;
|
||||
$params[$key] = $value;
|
||||
}
|
||||
$params = $request->getRouteParams();
|
||||
|
||||
$controllerName = $params['controller'] ?? null;
|
||||
$actionName = $params['action'] ?? null;
|
||||
@@ -107,9 +86,7 @@ class RouteProcessor
|
||||
unset($params['controller']);
|
||||
unset($params['action']);
|
||||
|
||||
$result = $this->controllerManager->process(
|
||||
$controllerName, $requestMethod, $actionName, $params, $request, $response
|
||||
) ?? null;
|
||||
$result = $this->controllerManager->process($controllerName, $actionName, $request, $response) ?? null;
|
||||
|
||||
$responseContents = $result;
|
||||
|
||||
|
||||
@@ -105,7 +105,10 @@ class Api implements ApplicationRunner
|
||||
$slim->$method(
|
||||
$route,
|
||||
function (Psr7Request $request, Psr7Response $response, array $args) use ($item, $slim) {
|
||||
$requestWrapped = new RequestWrapper($request, $slim->getBasePath(), $args);
|
||||
$routeParams = $this->getRouteParams($item, $args);
|
||||
|
||||
$requestWrapped = new RequestWrapper($request, $slim->getBasePath(), $routeParams);
|
||||
|
||||
$responseWrapped = new ResponseWrapper($response);
|
||||
|
||||
$this->processRequest($item, $requestWrapped, $responseWrapped, $args);
|
||||
@@ -115,6 +118,45 @@ class Api implements ApplicationRunner
|
||||
);
|
||||
}
|
||||
|
||||
protected function getRouteParams(array $item, array $args) : array
|
||||
{
|
||||
$params = [];
|
||||
|
||||
$routeParams = $item['params'] ?? [];
|
||||
|
||||
$paramKeys = array_keys($routeParams);
|
||||
|
||||
$setKeyList = [];
|
||||
|
||||
foreach ($paramKeys as $key) {
|
||||
$value = $routeParams[$key];
|
||||
|
||||
$paramName = $key;
|
||||
|
||||
if ($value[0] === ':') {
|
||||
$realKey = substr($value, 1);
|
||||
|
||||
$params[$paramName] = $args[$realKey];
|
||||
|
||||
$setKeyList[] = $realKey;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$params[$paramName] = $value;
|
||||
}
|
||||
|
||||
foreach ($args as $key => $value) {
|
||||
if (in_array($key, $setKeyList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$params[$key] = $value;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
protected function processRequest(array $item, RequestWrapper $requestWrapped, ResponseWrapper $responseWrapped, array $args)
|
||||
{
|
||||
try {
|
||||
@@ -141,7 +183,7 @@ class Api implements ApplicationRunner
|
||||
|
||||
ob_start();
|
||||
|
||||
$routeProcessor->process($item['route'], $item['params'], $requestWrapped, $responseWrapped, $args);
|
||||
$routeProcessor->process($item['route'], $requestWrapped, $responseWrapped);
|
||||
|
||||
ob_clean();
|
||||
}
|
||||
@@ -177,6 +219,7 @@ class Api implements ApplicationRunner
|
||||
|
||||
if (!in_array($method, $this->allowedMethodList)) {
|
||||
$GLOBALS['log']->warning("Route: Method '{$method}' is not supported. Fix the route '{$route}'.");
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -56,16 +56,12 @@ class ControllerManager
|
||||
$this->classFinder = $classFinder;
|
||||
}
|
||||
|
||||
public function process(
|
||||
string $controllerName,
|
||||
string $requestMethod,
|
||||
string $actionName,
|
||||
array $params,
|
||||
Request $request,
|
||||
Response $response
|
||||
) {
|
||||
public function process(string $controllerName, string $actionName, Request $request, Response $response)
|
||||
{
|
||||
$controller = $this->createController($controllerName);
|
||||
|
||||
$requestMethod = $request->getMethod();
|
||||
|
||||
if ($actionName == 'index') {
|
||||
$actionName = $controller::$defaultAction ?? 'index';
|
||||
}
|
||||
@@ -102,6 +98,8 @@ class ControllerManager
|
||||
$data = json_decode($data);
|
||||
}
|
||||
|
||||
$params = $request->getRouteParams();
|
||||
|
||||
$beforeMethodName = 'before' . $actionNameUcfirst;
|
||||
|
||||
if (method_exists($controller, $beforeMethodName)) {
|
||||
|
||||
@@ -173,7 +173,7 @@ abstract class BaseTestCase extends \PHPUnit\Framework\TestCase
|
||||
}
|
||||
|
||||
protected function createRequest(
|
||||
string $method, array $queryParams = [], array $headers = [], ?string $body = null
|
||||
string $method, array $queryParams = [], array $headers = [], ?string $body = null, array $routeParams = []
|
||||
) : RequestWrapper {
|
||||
$request = (new RequestFactory())->createRequest($method, 'http://localhost/?' . http_build_query($queryParams));
|
||||
|
||||
@@ -187,7 +187,7 @@ abstract class BaseTestCase extends \PHPUnit\Framework\TestCase
|
||||
);
|
||||
}
|
||||
|
||||
return new RequestWrapper($request);
|
||||
return new RequestWrapper($request, '', $routeParams);
|
||||
}
|
||||
|
||||
protected function createResponse()
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace tests\integration\Espo\User;
|
||||
|
||||
use Espo\Core\ControllerManager;
|
||||
|
||||
class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
{
|
||||
protected $dataFile = 'User/Login.php';
|
||||
@@ -47,7 +49,7 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
|
||||
public function testUserAccess0()
|
||||
{
|
||||
$this->expectException('\\Espo\\Core\\Exceptions\\Forbidden');
|
||||
$this->expectException('Espo\\Core\\Exceptions\\Forbidden');
|
||||
|
||||
$this->createUser('tester', array(
|
||||
'assignmentPermission' => 'team',
|
||||
@@ -77,18 +79,21 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
|
||||
$app = $this->createApplication();
|
||||
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$request = $this->createRequest('POST', [], ['Content-Type' => 'application/json']);
|
||||
$request = $this->createRequest(
|
||||
'POST',
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
'{"name":"Test Account"}'
|
||||
);
|
||||
|
||||
$data = json_decode('{"name":"Test Account"}');
|
||||
|
||||
$result = $controllerManager->process('Account', 'POST', 'create', [], $data, $request, $this->createResponse());
|
||||
$result = $controllerManager->process('Account', 'create', $request, $this->createResponse());
|
||||
}
|
||||
|
||||
public function testPortalUserAccess()
|
||||
{
|
||||
$this->expectException('\\Espo\\Core\\Exceptions\\Forbidden');
|
||||
$this->expectException('Espo\\Core\\Exceptions\\Forbidden');
|
||||
|
||||
$newUser = $this->createUser(array(
|
||||
'userName' => 'tester',
|
||||
@@ -117,12 +122,13 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
|
||||
$app = $this->createApplication();
|
||||
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$params = [];
|
||||
$data = json_decode('{"name":"Test Account"}');
|
||||
$request = $this->createRequest('POST', $params, ['Content-Type' => 'application/json']);
|
||||
$result = $controllerManager->process('Account', 'POST', 'create', $params, $data, $request, $this->createResponse());
|
||||
|
||||
$request = $this->createRequest('POST', [], ['Content-Type' => 'application/json'], '{"name":"Test Account"}');
|
||||
|
||||
$result = $controllerManager->process('Account', 'create', $request, $this->createResponse());
|
||||
}
|
||||
|
||||
public function testUserAccessEditOwn1()
|
||||
@@ -140,54 +146,68 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
$user2 = $this->createUser('test-2', []);
|
||||
|
||||
$this->auth('test-1');
|
||||
|
||||
$app = $this->createApplication();
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$params = [
|
||||
'id' => $user1->id
|
||||
'id' => $user1->id,
|
||||
];
|
||||
|
||||
$data = (object) [
|
||||
'id' => $user1->id,
|
||||
'title' => 'Test'
|
||||
];
|
||||
|
||||
$request = $this->createRequest('PATCH', $params, ['Content-Type' => 'application/json']);
|
||||
$request = $this->createRequest('PATCH', [], ['Content-Type' => 'application/json'], json_encode($data), $params);
|
||||
|
||||
$result = $controllerManager->process(
|
||||
'User', 'PATCH', 'update', $params, $data, $request, $this->createResponse());
|
||||
'User', 'update', $request, $this->createResponse()
|
||||
);
|
||||
|
||||
$this->assertTrue(is_object($result));
|
||||
|
||||
$params = [
|
||||
'id' => $user2->id
|
||||
'id' => $user2->id,
|
||||
];
|
||||
|
||||
$data = (object) [
|
||||
'id' => $user2->id,
|
||||
'title' => 'Test'
|
||||
];
|
||||
$request = $this->createRequest('PATCH', $params, ['Content-Type' => 'application/json']);
|
||||
|
||||
$request = $this->createRequest('PATCH', [], ['Content-Type' => 'application/json'], json_encode($data), $params);
|
||||
|
||||
$result = null;
|
||||
|
||||
try {
|
||||
$result = $controllerManager->process(
|
||||
'User', 'PATCH', 'update', $params, $data, $request, $this->createResponse());
|
||||
} catch (\Exception $e) {};
|
||||
'User', 'update', $request, $this->createResponse()
|
||||
);
|
||||
}
|
||||
catch (\Exception $e) {};
|
||||
|
||||
$this->assertNull($result);
|
||||
|
||||
|
||||
$params = [
|
||||
'id' => $user1->id
|
||||
];
|
||||
|
||||
$data = (object) [
|
||||
'id' => $user1->id,
|
||||
'type' => 'admin',
|
||||
'teamsIds' => ['id']
|
||||
];
|
||||
$request = $this->createRequest('PATCH', $params, ['Content-Type' => 'application/json']);
|
||||
$resultData = $controllerManager->process('User', 'PATCH', 'update', $params, $data, $request, $this->createResponse());
|
||||
|
||||
$request = $this->createRequest('PATCH', [], ['Content-Type' => 'application/json'], json_encode($data), $params);
|
||||
|
||||
$resultData = $controllerManager->process(
|
||||
'User', 'update', $request, $this->createResponse()
|
||||
);
|
||||
|
||||
$this->assertTrue(!property_exists($resultData, 'type') || $resultData->type !== 'admin');
|
||||
|
||||
$this->assertTrue(
|
||||
!property_exists($resultData, 'teamsIds') ||
|
||||
!is_array($resultData->teamsIds) || !in_array('id', $resultData->teamsIds)
|
||||
@@ -207,24 +227,30 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
]);
|
||||
|
||||
$this->auth('test-1');
|
||||
|
||||
$app = $this->createApplication();
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$params = [
|
||||
'id' => $user1->id
|
||||
];
|
||||
|
||||
$data = (object) [
|
||||
'id' => $user1->id,
|
||||
'title' => 'Test'
|
||||
];
|
||||
$request = $this->createRequest('PUT', $params, ['Content-Type' => 'application/json']);
|
||||
|
||||
$request = $this->createRequest('PUT', [], ['Content-Type' => 'application/json'], json_encode($data), $params);
|
||||
|
||||
$result = null;
|
||||
|
||||
try {
|
||||
$result = $controllerManager->process(
|
||||
'User', 'PUT', 'update', $params, $data, $request, $this->createResponse()
|
||||
'User', 'update', $request, $this->createResponse()
|
||||
);
|
||||
} catch (\Exception $e) {};
|
||||
}
|
||||
catch (\Exception $e) {};
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace tests\integration\Espo\Webhook;
|
||||
|
||||
use Espo\Core\ControllerManager;
|
||||
|
||||
class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
{
|
||||
|
||||
@@ -51,15 +53,13 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
|
||||
$app = $this->createApplication();
|
||||
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$this->expectException(\Espo\Core\Exceptions\Forbidden::class);
|
||||
|
||||
$request = $this->createRequest('POST', [], ['Content-Type' => 'application/json']);
|
||||
$request = $this->createRequest('POST', [], ['Content-Type' => 'application/json'], '{"event":"Account.create"}');
|
||||
|
||||
$data = json_decode('{"event":"Account.create"}');
|
||||
|
||||
$result = $controllerManager->process('Webhook', 'POST', 'create', [], $data, $request, $this->createResponse());
|
||||
$result = $controllerManager->process('Webhook', 'create', $request, $this->createResponse());
|
||||
}
|
||||
|
||||
public function testApiUserNoAccess1()
|
||||
@@ -78,22 +78,27 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
]
|
||||
);
|
||||
|
||||
$request = $this->createRequest('POST', [], [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Api-Key' => 'test-key',
|
||||
]);
|
||||
$request = $this->createRequest(
|
||||
'POST',
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Api-Key' => 'test-key',
|
||||
],
|
||||
'{"event":"Account.create", "url": "https://test"}'
|
||||
);
|
||||
|
||||
$this->auth(null, null, null, 'ApiKey', $request);
|
||||
|
||||
$data = json_decode('{"event":"Account.create", "url": "https://test"}');
|
||||
$data = json_decode();
|
||||
|
||||
$app = $this->createApplication();
|
||||
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$this->expectException(\Espo\Core\Exceptions\Forbidden::class);
|
||||
|
||||
$result = $controllerManager->process('Webhook', 'POST', 'create', [], $data, $request, $this->createResponse());
|
||||
$result = $controllerManager->process('Webhook', 'create', $request, $this->createResponse());
|
||||
}
|
||||
|
||||
public function testApiUserNoAccess2()
|
||||
@@ -113,22 +118,25 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
]
|
||||
);
|
||||
|
||||
$request = $this->createRequest('POST', [], [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Api-Key' => 'test-key',
|
||||
]);
|
||||
$request = $this->createRequest(
|
||||
'POST',
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Api-Key' => 'test-key',
|
||||
],
|
||||
'{"event":"Account.create", "url": "https://test"}'
|
||||
);
|
||||
|
||||
$this->auth(null, null, null, 'ApiKey', $request);
|
||||
|
||||
$data = json_decode('{"event":"Account.create", "url": "https://test"}');
|
||||
|
||||
$app = $this->createApplication();
|
||||
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$this->expectException(\Espo\Core\Exceptions\Forbidden::class);
|
||||
|
||||
$result = $controllerManager->process('Webhook', 'POST', 'create', [], $data, $request, $this->createResponse());
|
||||
$result = $controllerManager->process('Webhook', 'create', $request, $this->createResponse());
|
||||
}
|
||||
|
||||
public function testApiUserHasAccess1()
|
||||
@@ -148,20 +156,23 @@ class AclTest extends \tests\integration\Core\BaseTestCase
|
||||
]
|
||||
);
|
||||
|
||||
$request = $this->createRequest('POST', [], [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Api-Key' => 'test-key',
|
||||
]);
|
||||
$request = $this->createRequest(
|
||||
'POST',
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Api-Key' => 'test-key',
|
||||
],
|
||||
'{"event":"Account.create", "url": "https://test"}'
|
||||
);
|
||||
|
||||
$this->auth(null, null, null, 'ApiKey', $request);
|
||||
|
||||
$app = $this->createApplication();
|
||||
|
||||
$controllerManager = $app->getContainer()->get('controllerManager');
|
||||
$controllerManager = $app->getContainer()->get('injectableFactory')->create(ControllerManager::class);
|
||||
|
||||
$data = json_decode('{"event":"Account.create", "url": "https://test"}');
|
||||
|
||||
$result = $controllerManager->process('Webhook', 'POST', 'create', [], $data, $request, $this->createResponse());
|
||||
$result = $controllerManager->process('Webhook', 'create', $request, $this->createResponse());
|
||||
|
||||
$this->assertTrue(!empty($result));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user