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

migrate: Fix incorrect search url migration #928

Merged
merged 1 commit into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 5 additions & 22 deletions application/controllers/MigrateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,33 +82,16 @@ public function searchUrlAction()
$this->httpBadRequest('No JSON content');
}

$traverseFilter = function ($filter) use (&$traverseFilter) {
if ($filter instanceof Filter\Chain) {
foreach ($filter as $child) {
$newChild = $traverseFilter($child);
if ($newChild !== null) {
$filter->replace($child, $newChild);
}
}
} elseif ($filter instanceof Filter\Equal) {
if (strpos($filter->getValue(), '*') !== false) {
return Filter::like($filter->getColumn(), $filter->getValue());
}
} elseif ($filter instanceof Filter\Unequal) {
if (strpos($filter->getValue(), '*') !== false) {
return Filter::unlike($filter->getColumn(), $filter->getValue());
}
}
};

$urls = $this->getRequest()->getPost();

$result = [];
foreach ($urls as $urlString) {
$url = Url::fromPath($urlString);
$filter = QueryString::parse($url->getQueryString());
$filter = $traverseFilter($filter) ?? $filter;
$result[] = rawurldecode($url->setParams([])->setFilter($filter)->getAbsoluteUrl());
$params = $url->onlyWith(['sort', 'limit', 'view', 'columns', 'page'])->getParams();
$filter = $url->without(['sort', 'limit', 'view', 'columns', 'page'])->getParams();
$filter = QueryString::parse((string) $filter);
$filter = UrlMigrator::transformLegacyWildcardFilter($filter);
$result[] = rawurldecode($url->setParams($params)->setFilter($filter)->getAbsoluteUrl());
}

$response = $this->getResponse()->json();
Expand Down
233 changes: 161 additions & 72 deletions library/Icingadb/Compat/UrlMigrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Icinga\Module\Icingadb\Compat;

use Icinga\Web\UrlParams;
use InvalidArgumentException;
use ipl\Stdlib\Filter;
use ipl\Web\Filter\QueryString;
Expand Down Expand Up @@ -47,6 +48,11 @@ public static function isSupportedUrl(Url $url): bool
return isset($supportedPaths[ltrim($url->getPath(), '/')]);
}

public static function hasParamTransformer(string $name): bool
{
return method_exists(new self(), $name . 'Parameters');
}

public static function hasQueryTransformer(string $name): bool
{
return method_exists(new self(), $name . 'Columns');
Expand All @@ -58,22 +64,90 @@ public static function transformUrl(Url $url): Url
throw new InvalidArgumentException(sprintf('Url path "%s" is not supported', $url->getPath()));
}

list($queryTransformer, $dbRoute) = self::SUPPORTED_PATHS[ltrim($url->getPath(), '/')];
list($transformer, $dbRoute) = self::SUPPORTED_PATHS[ltrim($url->getPath(), '/')];

$url = clone $url;
$url->setPath($dbRoute);

if (! $url->getParams()->isEmpty()) {
$filter = QueryString::parse((string) $url->getParams());
$filter = self::transformFilter($filter, $queryTransformer);
if ($filter) {
$url->setParams([])->setFilter($filter);
[$params, $filter] = self::transformParams($url, $transformer);
$url->setParams($params);

if (! $filter->isEmpty()) {
$filter = QueryString::parse((string) $filter);
$filter = self::transformFilter($filter, $transformer);
$url->setFilter($filter ?: null);
}
}

return $url;
}

public static function transformParams(Url $url, string $transformerName = null): array
{
$transformer = new self();

$params = self::commonParameters();
$columns = self::commonColumns();

if ($transformerName !== null) {
if (! self::hasQueryTransformer($transformerName)) {
throw new InvalidArgumentException(sprintf('Transformer "%s" is not supported', $transformerName));
}

if (self::hasParamTransformer($transformerName)) {
$params = array_merge($params, $transformer->{$transformerName . 'Parameters'}());
}

$columns = array_merge($columns, $transformer->{$transformerName . 'Columns'}());
}

$columnRewriter = function ($column) use ($columns, $transformer) {
$rewritten = $transformer->rewrite(Filter::equal($column, 'bogus'), $columns);
if ($rewritten === false) {
return false;
} elseif ($rewritten instanceof Filter\Condition) {
return $rewritten->getColumn();
}

return $column;
};

$urlParams = $url->onlyWith(array_keys($params))->getParams();
$urlFilter = $url->without(array_keys($params))->getParams();

$newParams = new UrlParams();
foreach ($urlParams->toArray(false) as $name => $value) {
if (is_int($name)) {
$name = $value;
$value = true;
} else {
$value = rawurldecode($value);
}

$name = rawurldecode($name);

if (! isset($params[$name]) || $params[$name] === self::USE_EXPR) {
$newParams->add($name, $value);
} elseif ($params[$name] === self::DROP) {
// pass
} elseif (is_callable($params[$name])) {
$result = $params[$name]($value, $urlParams, $columnRewriter);
if ($result === false) {
continue;
} elseif (is_array($result)) {
[$name, $value] = $result;
} elseif ($result !== null) {
$value = $result;
}

$newParams->add($name, $value);
}
}

return [$newParams, $urlFilter];
}

/**
* Transform the given legacy filter
*
Expand All @@ -99,16 +173,39 @@ public static function transformFilter(Filter\Rule $filter, string $queryTransfo
return $rewritten === false ? false : ($rewritten instanceof Filter\Rule ? $rewritten : $filter);
}

/**
* Transform given legacy wildcard filters
*
* @param $filter Filter\Rule
*
* @return Filter\Chain|Filter\Condition
*/
public static function transformLegacyWildcardFilter(Filter\Rule $filter)
{
if ($filter instanceof Filter\Chain) {
foreach ($filter as $child) {
$newChild = self::transformLegacyWildcardFilter($child);
if ($newChild !== $child) {
$filter->replace($child, $newChild);
}
}

return $filter;
} else {
/** @var Filter\Condition $filter */
return self::transformWildcardFilter($filter);
}
}

/**
* Rewrite the given filter and legacy columns
*
* @param Filter\Rule $filter
* @param array $legacyColumns
* @param Filter\Chain|null $parent
*
* @return ?mixed
*/
protected function rewrite(Filter\Rule $filter, array $legacyColumns, Filter\Chain $parent = null)
protected function rewrite(Filter\Rule $filter, array $legacyColumns)
{
$rewritten = null;
if ($filter instanceof Filter\Condition) {
Expand Down Expand Up @@ -144,53 +241,17 @@ protected function rewrite(Filter\Rule $filter, array $legacyColumns, Filter\Cha
$filter->setValue($exprRule);
}

$rewritten = $this->transformWildcardFilter($rewritten);
} elseif ($column === 'sort') {
$column = $filter->getValue();
if (isset($legacyColumns[$column])) {
if ($legacyColumns[$column] === self::DROP) {
return false;
} elseif (! is_array($legacyColumns[$column])) {
return $rewritten;
}

$column = key($legacyColumns[$column]);

$rewritten = $filter->setValue($column);
}

if ($parent !== null) {
foreach ($parent as $child) {
if ($child instanceof Filter\Condition && $child->getColumn() === 'dir') {
$dir = $child->getValue();

$rewritten = $filter->setValue("{$column} {$dir}");

$parent->remove($child);
}
}
}
} elseif ($column === 'dir') {
if ($parent !== null) {
foreach ($parent as $child) {
if ($child instanceof Filter\Condition && $child->getColumn() === 'sort') {
return null;
}
}
}

return false;
} elseif (preg_match('/^_(host|service)_([\w.]+)/i', $column, $groups)) {
$rewritten = self::transformWildcardFilter($rewritten);
} elseif (preg_match('/^_(host|service)_(.+)/i', $column, $groups)) {
$rewritten = $filter->setColumn($groups[1] . '.vars.' . $groups[2]);
$rewritten = $this->transformWildcardFilter($rewritten);
$rewritten = self::transformWildcardFilter($rewritten);
}
} else {
/** @var Filter\Chain $filter */
foreach ($filter as $child) {
$retVal = $this->rewrite(
$child instanceof Filter\Condition ? clone $child : $child,
$legacyColumns,
$filter
$legacyColumns
);
if ($retVal === false) {
$filter->remove($child);
Expand All @@ -203,7 +264,7 @@ protected function rewrite(Filter\Rule $filter, array $legacyColumns, Filter\Cha
return $rewritten;
}

private function transformWildcardFilter(Filter\Condition $filter)
private static function transformWildcardFilter(Filter\Condition $filter)
{
if (is_string($filter->getValue()) && strpos($filter->getValue(), '*') !== false) {
if ($filter instanceof Filter\Equal) {
Expand All @@ -216,6 +277,31 @@ private function transformWildcardFilter(Filter\Condition $filter)
return $filter;
}

protected static function commonParameters(): array
{
return [
'sort' => function ($value, $params, $rewriter) {
$value = $rewriter($value);
if ($params->has('dir')) {
return "{$value} {$params->get('dir')}";
}

return $value;
},
'dir' => self::DROP,
'limit' => self::USE_EXPR,
'showCompact' => self::USE_EXPR,
'showFullscreen' => self::USE_EXPR,
'view' => function ($value) {
if ($value === 'compact') {
return ['showCompact', true];
}

return $value;
}
];
}

protected static function commonColumns(): array
{
return [
Expand Down Expand Up @@ -264,14 +350,11 @@ protected static function commonColumns(): array
];
}

protected static function hostsColumns(): array
protected static function hostsParameters(): array
{
return [

// Extraordinary columns
'addColumns' => function ($filter) {
/** @var Filter\Condition $filter */
$legacyColumns = array_filter(array_map('trim', explode(',', $filter->getValue())));
'addColumns' => function ($value, $params, $rewriter) {
$legacyColumns = array_filter(array_map('trim', explode(',', $value)));

$columns = [
'host.state.soft_state',
Expand All @@ -283,15 +366,20 @@ protected static function hostsColumns(): array
'host.state.is_problem'
];
foreach ($legacyColumns as $column) {
if (($c = self::transformFilter(Filter::equal($column, 'bogus'), 'hosts')) !== false) {
if ($c instanceof Filter\Condition) {
$columns[] = $c->getColumn();
}
$column = $rewriter($column);
if ($column !== false) {
$columns[] = $column;
}
}

return Filter::equal('columns', implode(',', $columns));
},
return ['columns', implode(',', $columns)];
}
];
}

protected static function hostsColumns(): array
{
return [

// Query columns
'host_acknowledged' => [
Expand Down Expand Up @@ -490,14 +578,11 @@ protected static function hostColumns(): array
];
}

protected static function servicesColumns(): array
protected static function servicesParameters(): array
{
return [

// Extraordinary columns
'addColumns' => function ($filter) {
/** @var Filter\Condition $filter */
$legacyColumns = array_filter(array_map('trim', explode(',', $filter->getValue())));
'addColumns' => function ($value, $params, $rewriter) {
$legacyColumns = array_filter(array_map('trim', explode(',', $value)));

$columns = [
'service.state.soft_state',
Expand All @@ -510,16 +595,20 @@ protected static function servicesColumns(): array
'service.state.is_problem'
];
foreach ($legacyColumns as $column) {
if (($c = self::transformFilter(Filter::equal($column, 'bogus'), 'services')) !== false) {
if ($c instanceof Filter\Condition) {
$columns[] = $c->getColumn();
}
$column = $rewriter($column);
if ($column !== false) {
$columns[] = $column;
}
}

return Filter::equal('columns', implode(',', $columns));
},
return ['columns', implode(',', $columns)];
}
];
}

protected static function servicesColumns(): array
{
return [
// Query columns
'host_acknowledged' => [
'host.state.is_acknowledged' => self::NO_YES
Expand Down
Loading