mirror of
https://github.com/nextcloud/server.git
synced 2026-04-29 10:03:32 -04:00
Merge pull request #42678 from nextcloud/refactor/app/remove-register-routes
refactor(App): Remove registerRoutes method
This commit is contained in:
commit
3e4ff2624c
10 changed files with 350 additions and 806 deletions
|
|
@ -6,9 +6,6 @@
|
|||
</NoInterfaceProperties>
|
||||
</file>
|
||||
<file src="lib/public/AppFramework/App.php">
|
||||
<InternalMethod>
|
||||
<code><![CDATA[new RouteConfig($this->container, $router, $routes)]]></code>
|
||||
</InternalMethod>
|
||||
<UndefinedClass>
|
||||
<code><![CDATA[\OC]]></code>
|
||||
</UndefinedClass>
|
||||
|
|
|
|||
|
|
@ -3491,11 +3491,6 @@
|
|||
<code><![CDATA[\OCA\Talk\Controller\PageController]]></code>
|
||||
</UndefinedClass>
|
||||
</file>
|
||||
<file src="lib/private/AppFramework/Routing/RouteConfig.php">
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$action['url-postfix']]]></code>
|
||||
</InvalidArrayOffset>
|
||||
</file>
|
||||
<file src="lib/private/AppFramework/Services/AppConfig.php">
|
||||
<MoreSpecificImplementedParamType>
|
||||
<code><![CDATA[$default]]></code>
|
||||
|
|
@ -4689,11 +4684,6 @@
|
|||
<code><![CDATA[$this->request->server]]></code>
|
||||
</NoInterfaceProperties>
|
||||
</file>
|
||||
<file src="lib/public/AppFramework/App.php">
|
||||
<InternalMethod>
|
||||
<code><![CDATA[new RouteConfig($this->container, $router, $routes)]]></code>
|
||||
</InternalMethod>
|
||||
</file>
|
||||
<file src="lib/public/AppFramework/Http/Response.php">
|
||||
<LessSpecificReturnStatement>
|
||||
<code><![CDATA[array_merge($mergeWith, $this->headers)]]></code>
|
||||
|
|
|
|||
|
|
@ -1034,7 +1034,6 @@ return array(
|
|||
'OC\\AppFramework\\OCS\\V1Response' => $baseDir . '/lib/private/AppFramework/OCS/V1Response.php',
|
||||
'OC\\AppFramework\\OCS\\V2Response' => $baseDir . '/lib/private/AppFramework/OCS/V2Response.php',
|
||||
'OC\\AppFramework\\Routing\\RouteActionHandler' => $baseDir . '/lib/private/AppFramework/Routing/RouteActionHandler.php',
|
||||
'OC\\AppFramework\\Routing\\RouteConfig' => $baseDir . '/lib/private/AppFramework/Routing/RouteConfig.php',
|
||||
'OC\\AppFramework\\Routing\\RouteParser' => $baseDir . '/lib/private/AppFramework/Routing/RouteParser.php',
|
||||
'OC\\AppFramework\\ScopedPsrLogger' => $baseDir . '/lib/private/AppFramework/ScopedPsrLogger.php',
|
||||
'OC\\AppFramework\\Services\\AppConfig' => $baseDir . '/lib/private/AppFramework/Services/AppConfig.php',
|
||||
|
|
|
|||
|
|
@ -1075,7 +1075,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\AppFramework\\OCS\\V1Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V1Response.php',
|
||||
'OC\\AppFramework\\OCS\\V2Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V2Response.php',
|
||||
'OC\\AppFramework\\Routing\\RouteActionHandler' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteActionHandler.php',
|
||||
'OC\\AppFramework\\Routing\\RouteConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteConfig.php',
|
||||
'OC\\AppFramework\\Routing\\RouteParser' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteParser.php',
|
||||
'OC\\AppFramework\\ScopedPsrLogger' => __DIR__ . '/../../..' . '/lib/private/AppFramework/ScopedPsrLogger.php',
|
||||
'OC\\AppFramework\\Services\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/AppConfig.php',
|
||||
|
|
|
|||
|
|
@ -1,279 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
namespace OC\AppFramework\Routing;
|
||||
|
||||
use OC\AppFramework\DependencyInjection\DIContainer;
|
||||
use OC\Route\Router;
|
||||
|
||||
/**
|
||||
* Class RouteConfig
|
||||
* @package OC\AppFramework\routing
|
||||
*/
|
||||
class RouteConfig {
|
||||
/** @var DIContainer */
|
||||
private $container;
|
||||
|
||||
/** @var Router */
|
||||
private $router;
|
||||
|
||||
/** @var array */
|
||||
private $routes;
|
||||
|
||||
/** @var string */
|
||||
private $appName;
|
||||
|
||||
/** @var string[] */
|
||||
private $controllerNameCache = [];
|
||||
|
||||
protected $rootUrlApps = [
|
||||
'cloud_federation_api',
|
||||
'core',
|
||||
'files_sharing',
|
||||
'files',
|
||||
'profile',
|
||||
'settings',
|
||||
'spreed',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param \OC\AppFramework\DependencyInjection\DIContainer $container
|
||||
* @param \OC\Route\Router $router
|
||||
* @param array $routes
|
||||
* @internal param $appName
|
||||
*/
|
||||
public function __construct(DIContainer $container, Router $router, $routes) {
|
||||
$this->routes = $routes;
|
||||
$this->container = $container;
|
||||
$this->router = $router;
|
||||
$this->appName = $container['AppName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* The routes and resource will be registered to the \OCP\Route\IRouter
|
||||
*/
|
||||
public function register() {
|
||||
// parse simple
|
||||
$this->processIndexRoutes($this->routes);
|
||||
|
||||
// parse resources
|
||||
$this->processIndexResources($this->routes);
|
||||
|
||||
/*
|
||||
* OCS routes go into a different collection
|
||||
*/
|
||||
$oldCollection = $this->router->getCurrentCollection();
|
||||
$this->router->useCollection($oldCollection . '.ocs');
|
||||
|
||||
// parse ocs simple routes
|
||||
$this->processOCS($this->routes);
|
||||
|
||||
// parse ocs simple routes
|
||||
$this->processOCSResources($this->routes);
|
||||
|
||||
$this->router->useCollection($oldCollection);
|
||||
}
|
||||
|
||||
private function processOCS(array $routes): void {
|
||||
$ocsRoutes = $routes['ocs'] ?? [];
|
||||
foreach ($ocsRoutes as $ocsRoute) {
|
||||
$this->processRoute($ocsRoute, 'ocs.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates one route base on the give configuration
|
||||
* @param array $routes
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
private function processIndexRoutes(array $routes): void {
|
||||
$simpleRoutes = $routes['routes'] ?? [];
|
||||
foreach ($simpleRoutes as $simpleRoute) {
|
||||
$this->processRoute($simpleRoute);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processRoute(array $route, string $routeNamePrefix = ''): void {
|
||||
$name = $route['name'];
|
||||
$postfix = $route['postfix'] ?? '';
|
||||
$root = $this->buildRootPrefix($route, $routeNamePrefix);
|
||||
|
||||
$url = $root . '/' . ltrim($route['url'], '/');
|
||||
$verb = strtoupper($route['verb'] ?? 'GET');
|
||||
|
||||
$split = explode('#', $name, 2);
|
||||
if (count($split) !== 2) {
|
||||
throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
|
||||
}
|
||||
[$controller, $action] = $split;
|
||||
|
||||
$controllerName = $this->buildControllerName($controller);
|
||||
$actionName = $this->buildActionName($action);
|
||||
|
||||
/*
|
||||
* The route name has to be lowercase, for symfony to match it correctly.
|
||||
* This is required because smyfony allows mixed casing for controller names in the routes.
|
||||
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
|
||||
* This is also safe on the PHP side because class and method names collide regardless of the casing.
|
||||
*/
|
||||
$routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix);
|
||||
|
||||
$router = $this->router->create($routeName, $url)
|
||||
->method($verb);
|
||||
|
||||
// optionally register requirements for route. This is used to
|
||||
// tell the route parser how url parameters should be matched
|
||||
if (array_key_exists('requirements', $route)) {
|
||||
$router->requirements($route['requirements']);
|
||||
}
|
||||
|
||||
// optionally register defaults for route. This is used to
|
||||
// tell the route parser how url parameters should be default valued
|
||||
$defaults = [];
|
||||
if (array_key_exists('defaults', $route)) {
|
||||
$defaults = $route['defaults'];
|
||||
}
|
||||
|
||||
$defaults['caller'] = [$this->appName, $controllerName, $actionName];
|
||||
$router->defaults($defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given name and url restful OCS routes are created:
|
||||
* - index
|
||||
* - show
|
||||
* - create
|
||||
* - update
|
||||
* - destroy
|
||||
*
|
||||
* @param array $routes
|
||||
*/
|
||||
private function processOCSResources(array $routes): void {
|
||||
$this->processResources($routes['ocs-resources'] ?? [], 'ocs.');
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given name and url restful routes are created:
|
||||
* - index
|
||||
* - show
|
||||
* - create
|
||||
* - update
|
||||
* - destroy
|
||||
*
|
||||
* @param array $routes
|
||||
*/
|
||||
private function processIndexResources(array $routes): void {
|
||||
$this->processResources($routes['resources'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given name and url restful routes are created:
|
||||
* - index
|
||||
* - show
|
||||
* - create
|
||||
* - update
|
||||
* - destroy
|
||||
*
|
||||
* @param array $resources
|
||||
* @param string $routeNamePrefix
|
||||
*/
|
||||
protected function processResources(array $resources, string $routeNamePrefix = ''): void {
|
||||
// declaration of all restful actions
|
||||
$actions = [
|
||||
['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
|
||||
['name' => 'show', 'verb' => 'GET'],
|
||||
['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
|
||||
['name' => 'update', 'verb' => 'PUT'],
|
||||
['name' => 'destroy', 'verb' => 'DELETE'],
|
||||
];
|
||||
|
||||
foreach ($resources as $resource => $config) {
|
||||
$root = $this->buildRootPrefix($config, $routeNamePrefix);
|
||||
|
||||
// the url parameter used as id to the resource
|
||||
foreach ($actions as $action) {
|
||||
$url = $root . '/' . ltrim($config['url'], '/');
|
||||
$method = $action['name'];
|
||||
|
||||
$verb = strtoupper($action['verb'] ?? 'GET');
|
||||
$collectionAction = $action['on-collection'] ?? false;
|
||||
if (!$collectionAction) {
|
||||
$url .= '/{id}';
|
||||
}
|
||||
if (isset($action['url-postfix'])) {
|
||||
$url .= '/' . $action['url-postfix'];
|
||||
}
|
||||
|
||||
$controller = $resource;
|
||||
|
||||
$controllerName = $this->buildControllerName($controller);
|
||||
$actionName = $this->buildActionName($method);
|
||||
|
||||
$routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method;
|
||||
|
||||
$route = $this->router->create($routeName, $url)
|
||||
->method($verb);
|
||||
|
||||
$route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function buildRootPrefix(array $route, string $routeNamePrefix): string {
|
||||
$defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
|
||||
$root = $route['root'] ?? $defaultRoot;
|
||||
|
||||
if ($routeNamePrefix !== '') {
|
||||
// In OCS all apps are whitelisted
|
||||
return $root;
|
||||
}
|
||||
|
||||
if (!\in_array($this->appName, $this->rootUrlApps, true)) {
|
||||
// Only allow root URLS for some apps
|
||||
return $defaultRoot;
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on a given route name the controller name is generated
|
||||
* @param string $controller
|
||||
* @return string
|
||||
*/
|
||||
private function buildControllerName(string $controller): string {
|
||||
if (!isset($this->controllerNameCache[$controller])) {
|
||||
$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
|
||||
}
|
||||
return $this->controllerNameCache[$controller];
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the action part of the route name the controller method name is generated
|
||||
* @param string $action
|
||||
* @return string
|
||||
*/
|
||||
private function buildActionName(string $action): string {
|
||||
return $this->underScoreToCamelCase($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Underscored strings are converted to camel case strings
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
private function underScoreToCamelCase(string $str): string {
|
||||
$pattern = '/_[a-z]?/';
|
||||
return preg_replace_callback(
|
||||
$pattern,
|
||||
function ($matches) {
|
||||
return strtoupper(ltrim($matches[0], '_'));
|
||||
},
|
||||
$str);
|
||||
}
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ class RouteParser {
|
|||
$url = $root . '/' . ltrim($route['url'], '/');
|
||||
$verb = strtoupper($route['verb'] ?? 'GET');
|
||||
|
||||
$split = explode('#', $name, 2);
|
||||
$split = explode('#', $name, 3);
|
||||
if (count($split) !== 2) {
|
||||
throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ class RouteParser {
|
|||
|
||||
/*
|
||||
* The route name has to be lowercase, for symfony to match it correctly.
|
||||
* This is required because smyfony allows mixed casing for controller names in the routes.
|
||||
* This is required because symfony allows mixed casing for controller names in the routes.
|
||||
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
|
||||
* This is also safe on the PHP side because class and method names collide regardless of the casing.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class Router implements IRouter {
|
|||
$this->loadedApps['core'] = true;
|
||||
$this->useCollection('root');
|
||||
$this->setupRoutes($this->getAttributeRoutes('core'), 'core');
|
||||
require __DIR__ . '/../../../core/routes.php';
|
||||
$this->requireRouteFile(__DIR__ . '/../../../core/routes.php', 'core');
|
||||
|
||||
// Also add the OCS collection
|
||||
$collection = $this->getCollection('root.ocs');
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCP\AppFramework;
|
||||
|
||||
use OC\AppFramework\Routing\RouteConfig;
|
||||
use OC\Route\Router;
|
||||
use OC\ServerContainer;
|
||||
use OCP\Route\IRouter;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -96,35 +93,6 @@ class App {
|
|||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is to be called to create single routes and restful routes based on the given $routes array.
|
||||
*
|
||||
* Example code in routes.php of tasks app (it will register two restful resources):
|
||||
* $routes = array(
|
||||
* 'resources' => array(
|
||||
* 'lists' => array('url' => '/tasklists'),
|
||||
* 'tasks' => array('url' => '/tasklists/{listId}/tasks')
|
||||
* )
|
||||
* );
|
||||
*
|
||||
* $a = new TasksApp();
|
||||
* $a->registerRoutes($this, $routes);
|
||||
*
|
||||
* @param \OCP\Route\IRouter $router
|
||||
* @param array $routes
|
||||
* @since 6.0.0
|
||||
* @suppress PhanAccessMethodInternal
|
||||
* @deprecated 20.0.0 Just return an array from your routes.php
|
||||
*/
|
||||
public function registerRoutes(IRouter $router, array $routes) {
|
||||
if (!($router instanceof Router)) {
|
||||
throw new \RuntimeException('Can only setup routes with real router');
|
||||
}
|
||||
|
||||
$routeConfig = new RouteConfig($this->container, $router, $routes);
|
||||
$routeConfig->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called by the routing component to fire up the frameworks dispatch mechanism.
|
||||
*
|
||||
|
|
|
|||
347
tests/lib/AppFramework/Routing/RouteParserTest.php
Normal file
347
tests/lib/AppFramework/Routing/RouteParserTest.php
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace Test\AppFramework\Routing;
|
||||
|
||||
use OC\AppFramework\Routing\RouteParser;
|
||||
use Symfony\Component\Routing\Route as RoutingRoute;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class RouteParserTest extends \Test\TestCase {
|
||||
|
||||
protected RouteParser $parser;
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->parser = new RouteParser();
|
||||
}
|
||||
|
||||
public function testParseRoutes(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
|
||||
['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open'));
|
||||
$this->assertArrayHasKey('app1.folders.create', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('app1.folders.create'));
|
||||
}
|
||||
|
||||
public function testParseRoutesRootApps(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
|
||||
['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'core');
|
||||
$this->assertArrayHasKey('core.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('core.folders.open'));
|
||||
$this->assertArrayHasKey('core.folders.create', $collection->all());
|
||||
$this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('core.folders.create'));
|
||||
}
|
||||
|
||||
public function testParseRoutesWithResources(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
|
||||
], 'resources' => [
|
||||
'names' => ['url' => '/names'],
|
||||
'folder_names' => ['url' => '/folder/names'],
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.open', $collection->all());
|
||||
$this->assertSimpleResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection);
|
||||
$this->assertSimpleResource('/apps/app1/names', 'names', 'NamesController', 'app1', $collection);
|
||||
}
|
||||
|
||||
public function testParseRoutesWithPostfix(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'],
|
||||
['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.update', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('app1.folders.update'));
|
||||
$this->assertArrayHasKey('app1.folders.update-edit', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('app1.folders.update-edit'));
|
||||
}
|
||||
|
||||
public function testParseRoutesKebabCaseAction(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.open_folder', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('app1.folders.open_folder'));
|
||||
}
|
||||
|
||||
public function testParseRoutesKebabCaseController(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.my_folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('app1.my_folders.open'));
|
||||
}
|
||||
|
||||
public function testParseRoutesLowercaseVerb(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.delete', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('app1.folders.delete'));
|
||||
}
|
||||
|
||||
public function testParseRoutesMissingVerb(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open'));
|
||||
}
|
||||
|
||||
public function testParseRoutesWithRequirements(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']]
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('app1.folders.open'));
|
||||
}
|
||||
|
||||
public function testParseRoutesWithDefaults(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']]
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('app1.folders.open'));
|
||||
}
|
||||
|
||||
public function testParseRoutesInvalidName(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$this->expectException(\UnexpectedValueException::class);
|
||||
$this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
}
|
||||
|
||||
public function testParseRoutesInvalidName2(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open#action', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$this->expectException(\UnexpectedValueException::class);
|
||||
$this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
}
|
||||
|
||||
public function testParseRoutesEmpty(): void {
|
||||
$routes = ['routes' => []];
|
||||
|
||||
$collection = $this->parser->parseDefaultRoutes($routes, 'app1');
|
||||
$this->assertEquals(0, $collection->count());
|
||||
}
|
||||
|
||||
// OCS routes
|
||||
|
||||
public function testParseOcsRoutes(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
|
||||
['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open'));
|
||||
$this->assertArrayHasKey('ocs.app1.folders.create', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('ocs.app1.folders.create'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesRootApps(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
|
||||
['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'core');
|
||||
$this->assertArrayHasKey('ocs.core.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('ocs.core.folders.open'));
|
||||
$this->assertArrayHasKey('ocs.core.folders.create', $collection->all());
|
||||
$this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('ocs.core.folders.create'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesWithPostfix(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'],
|
||||
['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.update', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update'));
|
||||
$this->assertArrayHasKey('ocs.app1.folders.update-edit', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update-edit'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesKebabCaseAction(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.open_folder', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('ocs.app1.folders.open_folder'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesKebabCaseController(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.my_folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('ocs.app1.my_folders.open'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesLowercaseVerb(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.delete', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('ocs.app1.folders.delete'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesMissingVerb(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open']
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesWithRequirements(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']]
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('ocs.app1.folders.open'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesWithDefaults(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']]
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
|
||||
$this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('ocs.app1.folders.open'));
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesInvalidName(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$this->expectException(\UnexpectedValueException::class);
|
||||
$this->parser->parseOCSRoutes($routes, 'app1');
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesEmpty(): void {
|
||||
$routes = ['ocs' => []];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertEquals(0, $collection->count());
|
||||
}
|
||||
|
||||
public function testParseOcsRoutesWithResources(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
|
||||
], 'ocs-resources' => [
|
||||
'names' => ['url' => '/names', 'root' => '/core/something'],
|
||||
'folder_names' => ['url' => '/folder/names'],
|
||||
]];
|
||||
|
||||
$collection = $this->parser->parseOCSRoutes($routes, 'app1');
|
||||
$this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
|
||||
$this->assertOcsResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection);
|
||||
$this->assertOcsResource('/core/something/names', 'names', 'NamesController', 'app1', $collection);
|
||||
}
|
||||
|
||||
protected function assertSimpleRoute(
|
||||
string $path,
|
||||
string $method,
|
||||
string $controller,
|
||||
string $action,
|
||||
string $app = 'app1',
|
||||
array $requirements = [],
|
||||
array $defaults = [],
|
||||
?RoutingRoute $route = null,
|
||||
): void {
|
||||
self::assertEquals($path, $route->getPath());
|
||||
self::assertEqualsCanonicalizing([$method], $route->getMethods());
|
||||
self::assertEqualsCanonicalizing($requirements, $route->getRequirements());
|
||||
self::assertEquals([...$defaults, 'action' => null, 'caller' => [$app, $controller, $action]], $route->getDefaults());
|
||||
}
|
||||
|
||||
protected function assertSimpleResource(
|
||||
string $path,
|
||||
string $resourceName,
|
||||
string $controller,
|
||||
string $app,
|
||||
RouteCollection $collection,
|
||||
): void {
|
||||
self::assertArrayHasKey("$app.$resourceName.index", $collection->all());
|
||||
self::assertArrayHasKey("$app.$resourceName.show", $collection->all());
|
||||
self::assertArrayHasKey("$app.$resourceName.create", $collection->all());
|
||||
self::assertArrayHasKey("$app.$resourceName.update", $collection->all());
|
||||
self::assertArrayHasKey("$app.$resourceName.destroy", $collection->all());
|
||||
|
||||
$this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("$app.$resourceName.index"));
|
||||
$this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("$app.$resourceName.create"));
|
||||
$this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("$app.$resourceName.show"));
|
||||
$this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("$app.$resourceName.update"));
|
||||
$this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("$app.$resourceName.destroy"));
|
||||
}
|
||||
|
||||
protected function assertOcsResource(
|
||||
string $path,
|
||||
string $resourceName,
|
||||
string $controller,
|
||||
string $app,
|
||||
RouteCollection $collection,
|
||||
): void {
|
||||
self::assertArrayHasKey("ocs.$app.$resourceName.index", $collection->all());
|
||||
self::assertArrayHasKey("ocs.$app.$resourceName.show", $collection->all());
|
||||
self::assertArrayHasKey("ocs.$app.$resourceName.create", $collection->all());
|
||||
self::assertArrayHasKey("ocs.$app.$resourceName.update", $collection->all());
|
||||
self::assertArrayHasKey("ocs.$app.$resourceName.destroy", $collection->all());
|
||||
|
||||
$this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("ocs.$app.$resourceName.index"));
|
||||
$this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("ocs.$app.$resourceName.create"));
|
||||
$this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("ocs.$app.$resourceName.show"));
|
||||
$this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("ocs.$app.$resourceName.update"));
|
||||
$this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("ocs.$app.$resourceName.destroy"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,477 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace Test\AppFramework\Routing;
|
||||
|
||||
use OC\AppFramework\DependencyInjection\DIContainer;
|
||||
use OC\AppFramework\Routing\RouteConfig;
|
||||
use OC\Route\Route;
|
||||
use OC\Route\Router;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\Diagnostics\IEventLogger;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use OCP\Route\IRouter;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class RoutingTest extends \Test\TestCase {
|
||||
public function testSimpleRoute(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET']
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
|
||||
}
|
||||
|
||||
public function testSimpleRouteWithUnderScoreNames(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'root' => '']
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'admin_folders.open_current', 'DELETE', '/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent', [], [], '', true);
|
||||
}
|
||||
|
||||
public function testSimpleOCSRoute(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET']
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
|
||||
}
|
||||
|
||||
public function testSimpleRouteWithMissingVerb(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open']
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
|
||||
}
|
||||
|
||||
public function testSimpleOCSRouteWithMissingVerb(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open']
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
|
||||
}
|
||||
|
||||
public function testSimpleRouteWithLowercaseVerb(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
|
||||
}
|
||||
|
||||
public function testSimpleOCSRouteWithLowercaseVerb(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open');
|
||||
}
|
||||
|
||||
public function testSimpleRouteWithRequirements(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'requirements' => ['something']]
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', ['something']);
|
||||
}
|
||||
|
||||
public function testSimpleOCSRouteWithRequirements(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'requirements' => ['something']]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', ['something']);
|
||||
}
|
||||
|
||||
public function testSimpleRouteWithDefaults(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', [], 'defaults' => ['param' => 'foobar']]
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], ['param' => 'foobar']);
|
||||
}
|
||||
|
||||
|
||||
public function testSimpleOCSRouteWithDefaults(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'defaults' => ['param' => 'foobar']]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], ['param' => 'foobar']);
|
||||
}
|
||||
|
||||
public function testSimpleRouteWithPostfix(): void {
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'postfix' => '_something']
|
||||
]];
|
||||
|
||||
$this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], [], '_something');
|
||||
}
|
||||
|
||||
public function testSimpleOCSRouteWithPostfix(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'postfix' => '_something']
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], [], '_something');
|
||||
}
|
||||
|
||||
|
||||
public function testSimpleRouteWithBrokenName(): void {
|
||||
$this->expectException(\UnexpectedValueException::class);
|
||||
|
||||
$routes = ['routes' => [
|
||||
['name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
|
||||
]];
|
||||
|
||||
/** @var IRouter|MockObject $router */
|
||||
$router = $this->getMockBuilder(Router::class)
|
||||
->onlyMethods(['create'])
|
||||
->setConstructorArgs([
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
$this->createMock(IEventLogger::class),
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
])
|
||||
->getMock();
|
||||
|
||||
// load route configuration
|
||||
$container = new DIContainer('app1');
|
||||
$config = new RouteConfig($container, $router, $routes);
|
||||
|
||||
$config->register();
|
||||
}
|
||||
|
||||
|
||||
public function testSimpleOCSRouteWithBrokenName(): void {
|
||||
$this->expectException(\UnexpectedValueException::class);
|
||||
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
|
||||
]];
|
||||
|
||||
/** @var IRouter|MockObject $router */
|
||||
$router = $this->getMockBuilder(Router::class)
|
||||
->onlyMethods(['create'])
|
||||
->setConstructorArgs([
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
$this->createMock(IEventLogger::class),
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
])
|
||||
->getMock();
|
||||
|
||||
// load route configuration
|
||||
$container = new DIContainer('app1');
|
||||
$config = new RouteConfig($container, $router, $routes);
|
||||
|
||||
$config->register();
|
||||
}
|
||||
|
||||
public function testSimpleOCSRouteWithUnderScoreNames(): void {
|
||||
$routes = ['ocs' => [
|
||||
['name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete']
|
||||
]];
|
||||
|
||||
$this->assertSimpleOCSRoute($routes, 'admin_folders.open_current', 'DELETE', '/apps/app1/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent');
|
||||
}
|
||||
|
||||
public function testOCSResource(): void {
|
||||
$routes = ['ocs-resources' => ['account' => ['url' => '/accounts']]];
|
||||
|
||||
$this->assertOCSResource($routes, 'account', '/apps/app1/accounts', 'AccountController', 'id');
|
||||
}
|
||||
|
||||
public function testOCSResourceWithUnderScoreName(): void {
|
||||
$routes = ['ocs-resources' => ['admin_accounts' => ['url' => '/admin/accounts']]];
|
||||
|
||||
$this->assertOCSResource($routes, 'admin_accounts', '/apps/app1/admin/accounts', 'AdminAccountsController', 'id');
|
||||
}
|
||||
|
||||
public function testOCSResourceWithRoot(): void {
|
||||
$routes = ['ocs-resources' => ['admin_accounts' => ['url' => '/admin/accounts', 'root' => '/core/endpoint']]];
|
||||
|
||||
$this->assertOCSResource($routes, 'admin_accounts', '/core/endpoint/admin/accounts', 'AdminAccountsController', 'id');
|
||||
}
|
||||
|
||||
public function testResource(): void {
|
||||
$routes = ['resources' => ['account' => ['url' => '/accounts']]];
|
||||
|
||||
$this->assertResource($routes, 'account', '/apps/app1/accounts', 'AccountController', 'id');
|
||||
}
|
||||
|
||||
public function testResourceWithUnderScoreName(): void {
|
||||
$routes = ['resources' => ['admin_accounts' => ['url' => '/admin/accounts']]];
|
||||
|
||||
$this->assertResource($routes, 'admin_accounts', '/apps/app1/admin/accounts', 'AdminAccountsController', 'id');
|
||||
}
|
||||
|
||||
private function assertSimpleRoute($routes, $name, $verb, $url, $controllerName, $actionName, array $requirements = [], array $defaults = [], $postfix = '', $allowRootUrl = false): void {
|
||||
if ($postfix) {
|
||||
$name .= $postfix;
|
||||
}
|
||||
|
||||
// route mocks
|
||||
$container = new DIContainer('app1');
|
||||
$route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults);
|
||||
|
||||
/** @var IRouter|MockObject $router */
|
||||
$router = $this->getMockBuilder(Router::class)
|
||||
->onlyMethods(['create'])
|
||||
->setConstructorArgs([
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
$this->createMock(IEventLogger::class),
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
])
|
||||
->getMock();
|
||||
|
||||
// we expect create to be called once:
|
||||
$router
|
||||
->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->equalTo('app1.' . $name), $this->equalTo($url))
|
||||
->willReturn($route);
|
||||
|
||||
// load route configuration
|
||||
$config = new RouteConfig($container, $router, $routes);
|
||||
if ($allowRootUrl) {
|
||||
self::invokePrivate($config, 'rootUrlApps', [['app1']]);
|
||||
}
|
||||
|
||||
$config->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $routes
|
||||
* @param string $name
|
||||
* @param string $verb
|
||||
* @param string $url
|
||||
* @param string $controllerName
|
||||
* @param string $actionName
|
||||
* @param array $requirements
|
||||
* @param array $defaults
|
||||
* @param string $postfix
|
||||
*/
|
||||
private function assertSimpleOCSRoute($routes,
|
||||
$name,
|
||||
$verb,
|
||||
$url,
|
||||
$controllerName,
|
||||
$actionName,
|
||||
array $requirements = [],
|
||||
array $defaults = [],
|
||||
$postfix = '') {
|
||||
if ($postfix) {
|
||||
$name .= $postfix;
|
||||
}
|
||||
|
||||
// route mocks
|
||||
$container = new DIContainer('app1');
|
||||
$route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults);
|
||||
|
||||
/** @var IRouter|MockObject $router */
|
||||
$router = $this->getMockBuilder(Router::class)
|
||||
->onlyMethods(['create'])
|
||||
->setConstructorArgs([
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
$this->createMock(IEventLogger::class),
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
])
|
||||
->getMock();
|
||||
|
||||
// we expect create to be called once:
|
||||
$router
|
||||
->expects($this->once())
|
||||
->method('create')
|
||||
->with($this->equalTo('ocs.app1.' . $name), $this->equalTo($url))
|
||||
->willReturn($route);
|
||||
|
||||
// load route configuration
|
||||
$config = new RouteConfig($container, $router, $routes);
|
||||
|
||||
$config->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $yaml
|
||||
* @param string $resourceName
|
||||
* @param string $url
|
||||
* @param string $controllerName
|
||||
* @param string $paramName
|
||||
*/
|
||||
private function assertOCSResource($yaml, $resourceName, $url, $controllerName, $paramName): void {
|
||||
/** @var IRouter|MockObject $router */
|
||||
$router = $this->getMockBuilder(Router::class)
|
||||
->onlyMethods(['create'])
|
||||
->setConstructorArgs([
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
$this->createMock(IEventLogger::class),
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
])
|
||||
->getMock();
|
||||
|
||||
// route mocks
|
||||
$container = new DIContainer('app1');
|
||||
$indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index');
|
||||
$showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show');
|
||||
$createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create');
|
||||
$updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update');
|
||||
$destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy');
|
||||
|
||||
$urlWithParam = $url . '/{' . $paramName . '}';
|
||||
|
||||
$calls = [
|
||||
['name' => 'ocs.app1.' . $resourceName . '.index', 'pattern' => $url, 'route' => $indexRoute],
|
||||
['name' => 'ocs.app1.' . $resourceName . '.show', 'pattern' => $urlWithParam, 'route' => $showRoute],
|
||||
['name' => 'ocs.app1.' . $resourceName . '.create', 'pattern' => $url, 'route' => $createRoute],
|
||||
['name' => 'ocs.app1.' . $resourceName . '.update', 'pattern' => $urlWithParam, 'route' => $updateRoute],
|
||||
['name' => 'ocs.app1.' . $resourceName . '.destroy', 'pattern' => $urlWithParam, 'route' => $destroyRoute],
|
||||
];
|
||||
|
||||
// we expect create to be called five times:
|
||||
$router
|
||||
->expects($this->exactly(5))
|
||||
->method('create')
|
||||
->willReturnCallback(function (string $name, string $pattern) use (&$calls) {
|
||||
$expected = array_shift($calls);
|
||||
$this->assertEquals($expected['name'], $name);
|
||||
$this->assertEquals($expected['pattern'], $pattern);
|
||||
return $expected['route'];
|
||||
});
|
||||
|
||||
// load route configuration
|
||||
$config = new RouteConfig($container, $router, $yaml);
|
||||
|
||||
$config->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resourceName
|
||||
* @param string $url
|
||||
* @param string $controllerName
|
||||
* @param string $paramName
|
||||
*/
|
||||
private function assertResource($yaml, $resourceName, $url, $controllerName, $paramName) {
|
||||
/** @var IRouter|MockObject $router */
|
||||
$router = $this->getMockBuilder(Router::class)
|
||||
->onlyMethods(['create'])
|
||||
->setConstructorArgs([
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
$this->createMock(IEventLogger::class),
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
])
|
||||
->getMock();
|
||||
|
||||
// route mocks
|
||||
$container = new DIContainer('app1');
|
||||
$indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index');
|
||||
$showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show');
|
||||
$createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create');
|
||||
$updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update');
|
||||
$destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy');
|
||||
|
||||
$urlWithParam = $url . '/{' . $paramName . '}';
|
||||
|
||||
$calls = [
|
||||
['name' => 'app1.' . $resourceName . '.index', 'pattern' => $url, 'route' => $indexRoute],
|
||||
['name' => 'app1.' . $resourceName . '.show', 'pattern' => $urlWithParam, 'route' => $showRoute],
|
||||
['name' => 'app1.' . $resourceName . '.create', 'pattern' => $url, 'route' => $createRoute],
|
||||
['name' => 'app1.' . $resourceName . '.update', 'pattern' => $urlWithParam, 'route' => $updateRoute],
|
||||
['name' => 'app1.' . $resourceName . '.destroy', 'pattern' => $urlWithParam, 'route' => $destroyRoute],
|
||||
];
|
||||
// we expect create to be called five times:
|
||||
$router
|
||||
->expects($this->exactly(5))
|
||||
->method('create')
|
||||
->willReturnCallback(function (string $name, string $pattern) use (&$calls) {
|
||||
$expected = array_shift($calls);
|
||||
$this->assertEquals($expected['name'], $name);
|
||||
$this->assertEquals($expected['pattern'], $pattern);
|
||||
return $expected['route'];
|
||||
});
|
||||
|
||||
// load route configuration
|
||||
$config = new RouteConfig($container, $router, $yaml);
|
||||
|
||||
$config->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DIContainer $container
|
||||
* @param string $verb
|
||||
* @param string $controllerName
|
||||
* @param string $actionName
|
||||
* @param array $requirements
|
||||
* @param array $defaults
|
||||
* @return MockObject
|
||||
*/
|
||||
private function mockRoute(
|
||||
DIContainer $container,
|
||||
$verb,
|
||||
$controllerName,
|
||||
$actionName,
|
||||
array $requirements = [],
|
||||
array $defaults = [],
|
||||
) {
|
||||
$route = $this->getMockBuilder(Route::class)
|
||||
->onlyMethods(['method', 'requirements', 'defaults'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$route
|
||||
->expects($this->once())
|
||||
->method('method')
|
||||
->with($this->equalTo($verb))
|
||||
->willReturn($route);
|
||||
|
||||
if (count($requirements) > 0) {
|
||||
$route
|
||||
->expects($this->once())
|
||||
->method('requirements')
|
||||
->with($this->equalTo($requirements))
|
||||
->willReturn($route);
|
||||
}
|
||||
|
||||
$route->expects($this->once())
|
||||
->method('defaults')
|
||||
->with($this->callback(function (array $def) use ($defaults, $controllerName, $actionName) {
|
||||
$defaults['caller'] = ['app1', $controllerName, $actionName];
|
||||
|
||||
$this->assertEquals($defaults, $def);
|
||||
return true;
|
||||
}))
|
||||
->willReturn($route);
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue