refactor route processing

This commit is contained in:
Yuri Kuznetsov
2020-09-11 12:33:35 +03:00
parent 63351ed6b7
commit ee53fffc67
8 changed files with 158 additions and 97 deletions

View File

@@ -63,6 +63,11 @@ interface Request
*/
public function getRouteParam(string $name) : ?string;
/**
* Get all route parameters.
*/
public function getRouteParams() : array;
/**
* Get a header value.
*/

View File

@@ -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());

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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()

View File

@@ -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);
}

View File

@@ -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));
}