icingaweb2-module-director/library/Director/RestApi/IcingaObjectsHandler.php

145 lines
4.0 KiB
PHP

<?php
namespace Icinga\Module\Director\RestApi;
use Exception;
use gipfl\Json\JsonString;
use Icinga\Application\Benchmark;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Data\Exporter;
use Icinga\Module\Director\Db\Cache\PrefetchCache;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Web\Table\ApplyRulesTable;
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) {
$this->sendJsonError($e);
}
}
/**
* @param ObjectsTable|ApplyRulesTable $table
* @return $this
*/
public function setTable($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;
}
/**
* @throws ProgrammingError
* @throws \Zend_Db_Select_Exception
* @throws \Zend_Db_Statement_Exception
*/
protected function streamJsonResult()
{
$this->response->setHeader('Content-Type', 'application/json', true);
$this->response->sendHeaders();
$connection = $this->db;
Benchmark::measure('Ready to stream JSON result');
$db = $connection->getDbAdapter();
$table = $this->getTable();
$exporter = new Exporter($connection);
$type = $table->getType();
RestApiParams::applyParamsToExporter($exporter, $this->request, $type);
$query = $table
->getQuery()
->reset(ZfSelect::COLUMNS)
->columns('*')
->reset(ZfSelect::LIMIT_COUNT)
->reset(ZfSelect::LIMIT_OFFSET);
if ($type === 'service' && $table instanceof ApplyRulesTable) {
$exporter->showIds();
}
echo '{ "objects": [ ';
$cnt = 0;
$objects = [];
$dummy = IcingaObject::createByType($type, [], $connection);
$dummy->prefetchAllRelatedTypes();
Benchmark::measure('Pre-fetching related objects');
PrefetchCache::initialize($this->db);
Benchmark::measure('Ready to query');
$stmt = $db->query($query);
$this->response->sendHeaders();
if (! ob_get_level()) {
ob_start();
}
$first = true;
$flushes = 0;
while ($row = $stmt->fetch()) {
/** @var IcingaObject $object */
if ($first) {
Benchmark::measure('Fetching first row');
}
$object = $dummy::fromDbRow($row, $connection);
$objects[] = JsonString::encode($exporter->export($object), 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->request->getUrl()->getParams()->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;
}
}