Implement Filter to IDO Sql parser

refs #4469
This commit is contained in:
Jannis Moßhammer 2013-10-10 14:18:39 +02:00
parent 24da98be83
commit dac61eda19
11 changed files with 228 additions and 11 deletions

View File

@ -71,13 +71,14 @@ class FilterController extends ActionController
$this->setupQueries();
$this->view->form->setRequest($this->getRequest());
if ($this->view->form->isSubmittedAndValid()) {
$tree = $this->registry->createQueryTreeForFilter($this->view->form->getValue('query'));
$this->view->tree = new \Icinga\Web\Widget\FilterBadgeRenderer($tree);
$view = \Icinga\Module\Monitoring\DataView\HostAndServiceStatus::fromRequest($this->getRequest());
$cv = new \Icinga\Module\Monitoring\Filter\Backend\IdoQueryConverter($view);
$this->view->sqlString = $cv->treeToSql($tree);
$this->view->params = $cv->getParams();
} else if ($this->getRequest()->getHeader('accept') == 'application/json') {
$this->getResponse()->setHeader('Content-Type', 'application/json');
$this->_helper->json($this->parse($this->getRequest()->getParam('query', '')));
}

View File

@ -4,4 +4,8 @@ echo $this->form;
if ($this->tree) {
echo $this->tree->render($this);
echo '<br/><pre><code>';
echo $this->sqlString;
echo '</pre></code>';
print_r($this->params);
}

View File

@ -0,0 +1,38 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Data;
interface Filterable
{
public function isValidFilterTarget($targetOrColumn);
public function resolveFilterTarget($targetOrColumn);
}

View File

@ -134,9 +134,6 @@ class Domain extends QueryProposer
foreach ($this->attributes as $attributeHandler) {
if ($attributeHandler->isValidQuery($query)) {
$node = $attributeHandler->convertToTreeNode($query);
if ($node) {
$node->context = $this->label;
}
return $node;
}
}

View File

@ -45,6 +45,7 @@ class Node
const OPERATOR_GREATER_EQ = '>=';
const OPERATOR_LESS_EQ = '<=';
const CONTEXT_TIMESTRING = 'timestring';
/**
* Array containing all possible operators
*
@ -90,6 +91,13 @@ class Node
*/
public $right;
/**
* Additional information for this node (like that it represents a date)
*
* @var mixed
*/
public $context;
/**
* Factory method for creating operator nodes
*

View File

@ -166,7 +166,9 @@ class TimeRangeSpecifier extends FilterType
if ($operator === null || $timeQuery === null) {
return null;
}
return Node::createOperatorNode($operator, $leftOperand, $timeQuery);
$node = Node::createOperatorNode($operator, $leftOperand, $timeQuery);
$node->context = Node::CONTEXT_TIMESTRING;
return $node;
}
/**

View File

@ -0,0 +1,81 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget;
use Icinga\Filter\Query\Tree;
use Icinga\Filter\Query\Node;
use Zend_View_Abstract;
class FilterBadgeRenderer implements Widget
{
private $tree;
private $conjunctionCellar = '';
public function __construct(Tree $tree)
{
$this->tree = $tree;
}
private function nodeToBadge(Node $node)
{
if ($node->type === Node::TYPE_OPERATOR) {
return ' <a class="btn btn-default btn-xs">'
. $this->conjunctionCellar . ' '
. ucfirst($node->left) . ' '
. $node->operator . ' '
. $node->right . '</a>';
}
$result = '';
$result .= $this->nodeToBadge($node->left);
$this->conjunctionCellar = $node->type;
$result .= $this->nodeToBadge($node->right);
return $result;
}
/**
* Renders this widget via the given view and returns the
* HTML as a string
*
* @param \Zend_View_Abstract $view
* @return string
*/
public function render(Zend_View_Abstract $view)
{
if ($this->tree->root == null) {
return '';
}
return $this->nodeToBadge($this->tree->root);
}
}

View File

@ -232,7 +232,7 @@ abstract class AbstractQuery extends Query
return array_key_exists($alias, $this->idxAliasColumn);
}
protected function aliasToColumnName($alias)
public function aliasToColumnName($alias)
{
return $this->idxAliasColumn[$alias];
}
@ -402,4 +402,15 @@ abstract class AbstractQuery extends Query
return $filter;
}
public function getMappedColumn($name)
{
foreach ($this->columnMap as $column => $results) {
if (isset($results[$name])) {
return $results[$name];
}
}
return null;
}
}

View File

@ -150,7 +150,7 @@ abstract class DataView
*
* @return bool
*/
protected function isValidFilterColumn($column)
public function isValidFilterColumn($column)
{
return in_array($column, $this->getColumns()) || in_array($column, $this->getFilterColumns());
}

View File

@ -121,7 +121,7 @@ class HostAndServiceStatus extends DataView
return array('hostgroups', 'servicegroups', 'service_problems');
}
protected function isValidFilterColumn($column)
public function isValidFilterColumn($column)
{
if ($column[0] === '_'
&& preg_match('/^_(?:host|service)_/', $column)

View File

@ -31,15 +31,90 @@ namespace Icinga\Module\Monitoring\Filter\Backend;
use Icinga\Filter\Query\Tree;
use Icinga\Filter\Query\Node;
use Icinga\Module\Monitoring\DataView\DataView;
class IdoQueryConverter
{
private $view;
private $query;
private $params = array();
public function getParams()
{
return $this->params;
}
public function __construct(DataView $view, array $initialParams = array())
{
$this->view = $view;
$this->query = $this->view->getQuery();
$this->params = $initialParams;
}
private function getSqlOperator($operator)
{
switch($operator) {
case Node::OPERATOR_EQUALS:
return 'LIKE';
case Node::OPERATOR_EQUALS_NOT:
return 'NOT LIKE';
default:
return $operator;
}
}
private function nodeToSqlQuery(Node $node)
{
if ($node->type !== Node::TYPE_OPERATOR) {
return $this->parseConjunctionNode($node);
} else {
return $this->parseOperatorNode($node);
}
}
private function parseConjunctionNode(Node $node)
{
$queryString = '';
$leftQuery = $this->nodeToSqlQuery($node->left);
$rightQuery = $this->nodeToSqlQuery($node->right);
if ($leftQuery != '') {
$queryString .= $leftQuery . ' ';
}
if ($rightQuery != '') {
$queryString .= (($queryString !== '') ? $node->type . ' ' : ' ') . $rightQuery;
}
return $queryString;
}
private function parseOperatorNode(Node $node)
{
if (!$this->view->isValidFilterColumn($node->left) && $this->query->getMappedColumn($node->left)) {
return '';
}
$queryString = $this->query->getMappedColumn($node->left);
$queryString .= ' ' . (is_integer($node->right) ? $node->operator : $this->getSqlOperator($node->operator));
$queryString .= ' ? ';
$this->params[] = $this->getParameterValue($node);
return $queryString;
}
private function getParameterValue(Node $node) {
switch($node->context) {
case Node::CONTEXT_TIMESTRING:
return strtotime($node->right);
default:
return $node->right;
}
}
public function treeToSql(Tree $tree)
{
if ($tree->root = null) {
if ($tree->root == null) {
return '';
}
return $this->nodeToSqlQuery($tree->root);
}
}