Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose domains #347

Merged
merged 1 commit into from Feb 3, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 13 additions & 2 deletions Command/DumpCommand.php
Expand Up @@ -101,6 +101,13 @@ protected function configure()
InputOption::VALUE_NONE,
'Pretty print the JSON.'
)
->addOption(
'domain',
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Specify expose domain',
array()
)
;
}

Expand Down Expand Up @@ -132,12 +139,15 @@ protected function execute(InputInterface $input, OutputInterface $output)
*/
private function doDump(InputInterface $input, OutputInterface $output)
{
$domain = $input->getOption('domain');

$extractor = $this->extractor;
$serializer = $this->serializer;
$targetPath = $input->getOption('target') ?:
sprintf(
'%s/../web/js/fos_js_routes.%s',
'%s/../web/js/fos_js_routes%s.%s',
$this->rootDir,
empty($domain) ? '' : ('_' . implode('_', $domain)),
$input->getOption('format')
);

Expand Down Expand Up @@ -168,7 +178,8 @@ private function doDump(InputInterface $input, OutputInterface $output)
$extractor->getPrefix($input->getOption('locale')),
$extractor->getHost(),
$extractor->getPort(),
$extractor->getScheme()
$extractor->getScheme(),
$domain
),
'json',
$params
Expand Down
28 changes: 27 additions & 1 deletion Command/RouterDebugExposedCommand.php
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Routing\RouteCollection;

/**
* A console command for retrieving information about exposed routes.
Expand Down Expand Up @@ -54,6 +55,7 @@ protected function configure()
new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'),
new InputOption('domain', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify expose domain', array())
))
->setName('fos:js-routing:debug')
->setDescription('Displays currently exposed routes for an application')
Expand Down Expand Up @@ -96,11 +98,35 @@ protected function execute(InputInterface $input, OutputInterface $output)
));
} else {
$helper = new DescriptorHelper();
$helper->describe($output, $this->extractor->getRoutes(), array(
$helper->describe($output, $this->getRoutes($input->getOption('domain')), array(
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'show_controllers' => $input->getOption('show-controllers'),
));
}
}

protected function getRoutes($domain = array())
{
$routes = $this->extractor->getRoutes();

if (empty($domain)) {
return $routes;
}

$targetRoutes = new RouteCollection();

foreach ($routes as $name => $route) {

$expose = $route->getOption('expose');
$expose = is_string($expose) ? ($expose === 'true' ? 'default' : $expose) : 'default';

if (in_array($expose, $domain, true)) {
$targetRoutes->add($name, $route);
}

}

return $targetRoutes;
}
}
3 changes: 2 additions & 1 deletion Controller/Controller.php
Expand Up @@ -98,7 +98,8 @@ public function indexAction(Request $request, $_format)
$this->exposedRoutesExtractor->getHost(),
$this->exposedRoutesExtractor->getPort(),
$this->exposedRoutesExtractor->getScheme(),
$request->getLocale()
$request->getLocale(),
$request->query->has('domain') ? explode(',', $request->query->get('domain')) : array()
);

$content = $this->serializer->serialize($routesResponse, 'json');
Expand Down
99 changes: 85 additions & 14 deletions Extractor/ExposedRoutesExtractor.php
Expand Up @@ -38,25 +38,37 @@ class ExposedRoutesExtractor implements ExposedRoutesExtractorInterface
*/
protected $bundles;

/**
* @var string
*/
protected $pattern;

/**
* @var array
*/
protected $routesToExpose;
protected $availableDomains;

/**
* Default constructor.
*
* @param RouterInterface $router The router.
* @param array $routesToExpose Some route names to expose.
* @param string $cacheDir
* @param array $bundles list of loaded bundles to check when generating the prefix
* @param RouterInterface $router The router.
* @param array $routesToExpose Some route names to expose.
* @param string $cacheDir
* @param array $bundles list of loaded bundles to check when generating the prefix
*
* @throws \Exception
*/
public function __construct(RouterInterface $router, array $routesToExpose = array(), $cacheDir, $bundles = array())
{
$this->router = $router;
$this->routesToExpose = $routesToExpose;
$this->cacheDir = $cacheDir;
$this->bundles = $bundles;

$domainPatterns = $this->extractDomainPatterns($routesToExpose);

$this->availableDomains = array_keys($domainPatterns);

$this->pattern = $this->buildPattern($domainPatterns);
}

/**
Expand All @@ -69,9 +81,27 @@ public function getRoutes()

/** @var Route $route */
foreach ($collection->all() as $name => $route) {
if ($this->isRouteExposed($route, $name)) {

if ($route->hasOption('expose')) {
$routes->add($name, $route);
continue;
}

preg_match('#' . $this->pattern . '#', $name, $matches);

if (count($matches) === 0) {
continue;
}

$domain = $this->getDomainByRouteMatches($matches, $name);

if (is_null($domain)) {
continue;
}

$route = clone $route;
$route->setOption('expose', $domain);
$routes->add($name, $route);
}

return $routes;
Expand Down Expand Up @@ -166,23 +196,64 @@ public function getResources()
*/
public function isRouteExposed(Route $route, $name)
{
$pattern = $this->buildPattern();
return true === $route->hasOption('expose') ||
('' !== $this->pattern && preg_match('#' . $this->pattern . '#', $name));
}

return true === $route->getOption('expose')
|| 'true' === $route->getOption('expose')
|| ('' !== $pattern && preg_match('#' . $pattern . '#', $name));
protected function getDomainByRouteMatches($matches, $name)
{
$matches = array_filter($matches, function($match) {
return !empty($match);
});

$matches = array_flip(array_intersect_key($matches, array_flip($this->availableDomains)));

return isset($matches[$name]) ? $matches[$name] : null;
}

protected function extractDomainPatterns($routesToExpose)
{
$domainPatterns = array();

foreach ($routesToExpose as $item) {

if (is_string($item)) {
$domainPatterns['default'][] = $item;
continue;
}

if (is_array($item) && is_string($item['pattern'])) {

if (!isset($item['domain'])) {
$domainPatterns['default'][] = $item['pattern'];
continue;
} elseif (is_string($item['domain'])) {
$domainPatterns[$item['domain']][] = $item['pattern'];
continue;
}

}

throw new \Exception('routes_to_expose definition is invalid');
}

return $domainPatterns;
}

/**
* Convert the routesToExpose array in a regular expression pattern
*
* @param $domainPatterns
* @return string
* @throws \Exception
*/
protected function buildPattern()
protected function buildPattern($domainPatterns)
{
$patterns = array();
foreach ($this->routesToExpose as $toExpose) {
$patterns[] = '(' . $toExpose . ')';

foreach ($domainPatterns as $domain => $items) {

$patterns[] = '(?P<' . $domain . '>' . implode($items, '|') . ')';
}

return implode($patterns, '|');
Expand Down
22 changes: 21 additions & 1 deletion Response/RoutesResponse.php
Expand Up @@ -22,8 +22,10 @@ class RoutesResponse
private $port;
private $scheme;
private $locale;
private $domains;

public function __construct($baseUrl, RouteCollection $routes = null, $prefix = null, $host = null, $port = null, $scheme = null, $locale = null)
public function __construct($baseUrl, RouteCollection $routes = null, $prefix = null, $host = null, $port = null,
$scheme = null, $locale = null, $domains = array())
{
$this->baseUrl = $baseUrl;
$this->routes = $routes ?: new RouteCollection();
Expand All @@ -32,6 +34,7 @@ public function __construct($baseUrl, RouteCollection $routes = null, $prefix =
$this->port = $port;
$this->scheme = $scheme;
$this->locale = $locale;
$this->domains = $domains;
}

public function getBaseUrl()
Expand All @@ -43,6 +46,23 @@ public function getRoutes()
{
$exposedRoutes = array();
foreach ($this->routes->all() as $name => $route) {

if (!$route->hasOption('expose')) {
$domain = 'default';
} else {
$domain = $route->getOption('expose');
$domain = is_string($domain) ? ($domain === 'true' ? 'default' : $domain) : 'default';
}


if (count($this->domains) === 0) {
if ($domain !== 'default') {
continue;
}
} elseif (!in_array($domain, $this->domains, true)) {
continue;
}

$compiledRoute = $route->compile();
$defaults = array_intersect_key(
$route->getDefaults(),
Expand Down
43 changes: 43 additions & 0 deletions Tests/Controller/ControllerTest.php
Expand Up @@ -157,6 +157,49 @@ public function testCacheControl()
$this->assertEquals(456, $response->headers->getCacheControlDirective('s-maxage'));
}

public function testExposeDomain()
{
$routes = new RouteCollection();
$routes->add('homepage', new Route('/'));
$routes->add('admin_index', new Route('/admin', array(), array(),
array('expose' => 'admin')));
$routes->add('admin_pages', new Route('/admin/path', array(), array(),
array('expose' => 'admin')));
$routes->add('blog_index', new Route('/blog', array(), array(),
array('expose' => 'blog'), 'localhost'));
$routes->add('blog_post', new Route('/blog/{slug}', array(), array(),
array('expose' => 'blog'), 'localhost'));

$controller = new Controller(
$this->getSerializer(),
$this->getExtractor($routes)
);

$response = $controller->indexAction($this->getRequest('/'), 'json');

$this->assertEquals('{"base_url":"","routes":{"homepage":{"tokens":[["text","\/"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]}},"prefix":"","host":"","port":null,"scheme":""}', $response->getContent());

$response = $controller->indexAction($this->getRequest('/',
'GET', array('domain' => 'admin')), 'json');

$this->assertEquals('{"base_url":"","routes":{"admin_index":{"tokens":[["text","\/admin"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"admin_pages":{"tokens":[["text","\/admin\/path"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]}},"prefix":"","host":"","port":null,"scheme":""}', $response->getContent());

$response = $controller->indexAction($this->getRequest('/',
'GET', array('domain' => 'blog')), 'json');

$this->assertEquals('{"base_url":"","routes":{"blog_index":{"tokens":[["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]},"blog_post":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]}},"prefix":"","host":"","port":null,"scheme":""}', $response->getContent());

$response = $controller->indexAction($this->getRequest('/',
'GET', array('domain' => 'admin,blog')), 'json');

$this->assertEquals('{"base_url":"","routes":{"admin_index":{"tokens":[["text","\/admin"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"admin_pages":{"tokens":[["text","\/admin\/path"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"blog_index":{"tokens":[["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]},"blog_post":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]}},"prefix":"","host":"","port":null,"scheme":""}', $response->getContent());

$response = $controller->indexAction($this->getRequest('/',
'GET', array('domain' => 'default,admin,blog')), 'json');

$this->assertEquals('{"base_url":"","routes":{"homepage":{"tokens":[["text","\/"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"admin_index":{"tokens":[["text","\/admin"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"admin_pages":{"tokens":[["text","\/admin\/path"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"blog_index":{"tokens":[["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]},"blog_post":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]}},"prefix":"","host":"","port":null,"scheme":""}', $response->getContent());
}

private function getExtractor(RouteCollection $exposedRoutes = null, $baseUrl = '')
{
if (null === $exposedRoutes) {
Expand Down
3 changes: 3 additions & 0 deletions Tests/Extractor/ExposedRoutesExtractorTest.php
Expand Up @@ -44,6 +44,9 @@ public function testGetRoutes()

$router = $this->getRouter($expected);
$extractor = new ExposedRoutesExtractor($router, array('.*'), $this->cacheDir, array());

$expected->addOptions(array('expose' => 'default'));

$this->assertEquals($expected, $extractor->getRoutes());
}

Expand Down