IcingaObjectsHandler: new REST API handler for...

...list of objects
This commit is contained in:
Thomas Gelf 2017-08-16 08:17:31 +02:00
parent b364165110
commit 408ff7639c
2 changed files with 150 additions and 89 deletions

View File

@ -0,0 +1,139 @@
<?php
namespace Icinga\Module\Director\RestApi;
use Exception;
use Icinga\Application\Benchmark;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Db\Cache\PrefetchCache;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Web\Table\ObjectsTable;
use Zend_Db_Select as ZfSelect;
class IcingaObjectsHandler extends RequestHandler
{
/** @var ObjectsTable */
protected $table;
public function processApiRequest()
{
try {
$this->streamJsonResult();
} catch (Exception $e) {
// NONO
$this->sendJsonError($e->getTraceAsString());
}
}
public function setTable(ObjectsTable $table)
{
$this->table = $table;
return $this;
}
/**
* @return ObjectsTable
* @throws ProgrammingError
*/
protected function getTable()
{
if ($this->table === null) {
throw new ProgrammingError('Table is required');
}
return $this->table;
}
protected function streamJsonResult()
{
$connection = $this->db;
Benchmark::measure('aha');
$db = $connection->getDbAdapter();
$table = $this->getTable();
$query = $table
->getQuery()
->reset(ZfSelect::COLUMNS)
->columns('*')
->reset(ZfSelect::LIMIT_COUNT)
->reset(ZfSelect::LIMIT_OFFSET);
echo '{ "objects": [ ';
$cnt = 0;
$objects = [];
$dummy = IcingaObject::createByType($table->getType(), [], $connection);
$dummy->prefetchAllRelatedTypes();
Benchmark::measure('Prefetching');
PrefetchCache::initialize($this->db);
Benchmark::measure('Ready to query');
$stmt = $db->query($query);
$this->response->sendHeaders();
if (! ob_get_level()) {
ob_start();
}
$params = $this->request->getUrl()->getParams();
$resolved = (bool) $params->get('resolved', false);
$withNull = ! $params->shift('withNull');
$properties = $params->shift('properties');
if (strlen($properties)) {
$properties = preg_split('/\s*,\s*/', $properties, -1, PREG_SPLIT_NO_EMPTY);
} else {
$properties = null;
}
$first = true;
$flushes = 0;
while ($row = $stmt->fetch()) {
/** @var IcingaObject $object */
if ($first) {
Benchmark::measure('First row');
}
$object = $dummy::fromDbRow($row, $connection);
$objects[] = json_encode($object->toPlainObject(
$resolved,
$withNull,
$properties
), JSON_PRETTY_PRINT);
if ($first) {
Benchmark::measure('Got first row');
$first = false;
}
$cnt++;
if ($cnt === 100) {
if ($flushes > 0) {
echo ', ';
}
echo implode(', ', $objects);
$cnt = 0;
$objects = [];
$flushes++;
ob_end_flush();
ob_start();
}
}
if ($cnt > 0) {
if ($flushes > 0) {
echo ', ';
}
echo implode(', ', $objects);
}
if ($params->get('benchmark')) {
echo "],\n";
Benchmark::measure('All done');
echo '"benchmark_string": ' . json_encode(Benchmark::renderToText());
} else {
echo '] ';
}
echo "}\n";
if (ob_get_level()) {
ob_end_flush();
}
// TODO: can we improve this?
exit;
}
}

View File

@ -2,15 +2,13 @@
namespace Icinga\Module\Director\Web\Controller; namespace Icinga\Module\Director\Web\Controller;
use Icinga\Application\Benchmark;
use Icinga\Application\Icinga;
use Icinga\Data\Filter\FilterChain; use Icinga\Data\Filter\FilterChain;
use Icinga\Data\Filter\FilterExpression; use Icinga\Data\Filter\FilterExpression;
use Icinga\Exception\NotFoundError; use Icinga\Exception\NotFoundError;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Module\Director\Db\Cache\PrefetchCache;
use Icinga\Module\Director\Forms\IcingaMultiEditForm; use Icinga\Module\Director\Forms\IcingaMultiEditForm;
use Icinga\Module\Director\Objects\IcingaObject; use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\RestApi\IcingaObjectsHandler;
use Icinga\Module\Director\Web\ActionBar\ObjectsActionBar; use Icinga\Module\Director\Web\ActionBar\ObjectsActionBar;
use Icinga\Module\Director\Web\ActionBar\TemplateActionBar; use Icinga\Module\Director\Web\ActionBar\TemplateActionBar;
use Icinga\Module\Director\Web\Table\ApplyRulesTable; use Icinga\Module\Director\Web\Table\ApplyRulesTable;
@ -20,7 +18,6 @@ use Icinga\Module\Director\Web\Table\TemplatesTable;
use Icinga\Module\Director\Web\Tabs\ObjectsTabs; use Icinga\Module\Director\Web\Tabs\ObjectsTabs;
use Icinga\Module\Director\Web\Tree\TemplateTreeRenderer; use Icinga\Module\Director\Web\Tree\TemplateTreeRenderer;
use ipl\Html\Link; use ipl\Html\Link;
use Zend_Db_Select as ZfSelect;
abstract class ObjectsController extends ActionController abstract class ObjectsController extends ActionController
{ {
@ -50,15 +47,19 @@ abstract class ObjectsController extends ActionController
return $this; return $this;
} }
protected function apiRequestHandler()
{
return (new IcingaObjectsHandler(
$this->getRequest(),
$this->getResponse(),
$this->db()
))->setTable($this->getTable());
}
public function indexAction() public function indexAction()
{ {
if ($this->getRequest()->isApiRequest()) { if ($this->getRequest()->isApiRequest()) {
try { $this->apiRequestHandler()->dispatch();
$this->streamJsonResult();
} catch (\Exception $e) {
echo $e->getTraceAsString();
exit;
}
return; return;
} }
$type = $this->getType(); $type = $this->getType();
@ -77,85 +78,6 @@ abstract class ObjectsController extends ActionController
$this->table->renderTo($this); $this->table->renderTo($this);
} }
protected function streamJsonResult()
{
$connection = $this->db();
Benchmark::measure('aha');
$db = $connection->getDbAdapter();
$query = $this->getTable()
->getQuery()
->reset(ZfSelect::COLUMNS)
->columns('*')
->reset(ZfSelect::LIMIT_COUNT)
->reset(ZfSelect::LIMIT_OFFSET);
echo '{ "objects": [ ';
$cnt = 0;
$objects = [];
$dummy = IcingaObject::createByType($this->getType(), [], $connection);
$dummy->prefetchAllRelatedTypes();
Benchmark::measure('Prefetching');
PrefetchCache::initialize($this->db());
Benchmark::measure('Ready to query');
$stmt = $db->query($query);
$this->getResponse()->sendHeaders();
if (! ob_get_level()) {
ob_start();
}
$resolved = (bool) $this->params->get('resolved', false);
$first = true;
$flushes = 0;
while ($row = $stmt->fetch()) {
/** @var IcingaObject $object */
if ($first) {
Benchmark::measure('First row');
}
$object = $dummy::fromDbRow($row, $connection);
$objects[] = json_encode($object->toPlainObject($resolved, true), JSON_PRETTY_PRINT);
if ($first) {
Benchmark::measure('Got first row');
$first = false;
}
$cnt++;
if ($cnt === 100) {
if ($flushes > 0) {
echo ', ';
}
echo implode(', ', $objects);
$cnt = 0;
$objects = [];
$flushes++;
ob_end_flush();
ob_start();
}
}
if ($cnt > 0) {
if ($flushes > 0) {
echo ', ';
}
echo implode(', ', $objects);
}
if ($this->params->get('benchmark')) {
echo "],\n";
Benchmark::measure('All done');
echo '"benchmark_string": ' . json_encode(Benchmark::renderToText());
} else {
echo '] ';
}
echo "}\n";
if (ob_get_level()) {
ob_end_flush();
}
// TODO: can we improve this?
exit;
}
protected function getTable() protected function getTable()
{ {
return ObjectsTable::create($this->getType(), $this->db()) return ObjectsTable::create($this->getType(), $this->db())