Skip to content

Commit 06da58c

Browse files
authored
Fix history exports (#985)
fixes #955
2 parents 018d45f + 95849d9 commit 06da58c

10 files changed

+348
-153
lines changed

library/Icingadb/Data/CsvResultSet.php

Lines changed: 3 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,82 +4,9 @@
44

55
namespace Icinga\Module\Icingadb\Data;
66

7-
use DateTime;
8-
use DateTimeZone;
9-
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
10-
use ipl\Orm\Model;
11-
use ipl\Orm\Query;
7+
use ipl\Orm\ResultSet;
128

13-
class CsvResultSet extends VolatileStateResults
9+
class CsvResultSet extends ResultSet
1410
{
15-
protected $isCacheDisabled = true;
16-
17-
/**
18-
* @return array<string, ?string>
19-
*/
20-
public function current(): array
21-
{
22-
return $this->extractKeysAndValues(parent::current());
23-
}
24-
25-
protected function formatValue(string $key, $value): ?string
26-
{
27-
if (
28-
$value
29-
&& (
30-
$key === 'id'
31-
|| substr($key, -3) === '_id'
32-
|| substr($key, -3) === '.id'
33-
|| substr($key, -9) === '_checksum'
34-
|| substr($key, -4) === '_bin'
35-
)
36-
) {
37-
$value = bin2hex($value);
38-
}
39-
40-
if (is_bool($value)) {
41-
return $value ? 'true' : 'false';
42-
} elseif (is_string($value)) {
43-
return '"' . str_replace('"', '""', $value) . '"';
44-
} elseif (is_array($value)) {
45-
return '"' . implode(',', $value) . '"';
46-
} elseif ($value instanceof DateTime) {
47-
return $value->setTimezone(new DateTimeZone('UTC'))
48-
->format('Y-m-d\TH:i:s.vP');
49-
} else {
50-
return $value;
51-
}
52-
}
53-
54-
protected function extractKeysAndValues(Model $model, string $path = ''): array
55-
{
56-
$keysAndValues = [];
57-
foreach ($model as $key => $value) {
58-
$keyPath = ($path ? $path . '.' : '') . $key;
59-
if ($value instanceof Model) {
60-
$keysAndValues += $this->extractKeysAndValues($value, $keyPath);
61-
} else {
62-
$keysAndValues[$keyPath] = $this->formatValue($key, $value);
63-
}
64-
}
65-
66-
return $keysAndValues;
67-
}
68-
69-
public static function stream(Query $query): void
70-
{
71-
$query->setResultSetClass(__CLASS__);
72-
73-
foreach ($query as $i => $keysAndValues) {
74-
if ($i === 0) {
75-
echo implode(',', array_keys($keysAndValues));
76-
}
77-
78-
echo "\r\n";
79-
80-
echo implode(',', array_values($keysAndValues));
81-
}
82-
83-
exit;
84-
}
11+
use CsvResultSetUtils;
8512
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Icingadb\Data;
6+
7+
use DateTime;
8+
use DateTimeZone;
9+
use Icinga\Module\Icingadb\Model\Host;
10+
use Icinga\Module\Icingadb\Model\Service;
11+
use ipl\Orm\Model;
12+
use ipl\Orm\Query;
13+
14+
trait CsvResultSetUtils
15+
{
16+
/**
17+
* @return array<string, ?string>
18+
*/
19+
public function current(): array
20+
{
21+
return $this->extractKeysAndValues(parent::current());
22+
}
23+
24+
protected function formatValue(string $key, $value): ?string
25+
{
26+
if (
27+
$value
28+
&& (
29+
$key === 'id'
30+
|| substr($key, -3) === '_id'
31+
|| substr($key, -3) === '.id'
32+
|| substr($key, -9) === '_checksum'
33+
|| substr($key, -4) === '_bin'
34+
)
35+
) {
36+
$value = bin2hex($value);
37+
}
38+
39+
if (is_bool($value)) {
40+
return $value ? 'true' : 'false';
41+
} elseif (is_string($value)) {
42+
return '"' . str_replace('"', '""', $value) . '"';
43+
} elseif (is_array($value)) {
44+
return '"' . implode(',', $value) . '"';
45+
} elseif ($value instanceof DateTime) {
46+
return $value->setTimezone(new DateTimeZone('UTC'))
47+
->format('Y-m-d\TH:i:s.vP');
48+
} else {
49+
return $value;
50+
}
51+
}
52+
53+
protected function extractKeysAndValues(Model $model, string $path = ''): array
54+
{
55+
$keysAndValues = [];
56+
foreach ($model as $key => $value) {
57+
$keyPath = ($path ? $path . '.' : '') . $key;
58+
if ($value instanceof Model) {
59+
$keysAndValues += $this->extractKeysAndValues($value, $keyPath);
60+
} else {
61+
$keysAndValues[$keyPath] = $this->formatValue($key, $value);
62+
}
63+
}
64+
65+
return $keysAndValues;
66+
}
67+
68+
public static function stream(Query $query): void
69+
{
70+
if ($query->getModel() instanceof Host || $query->getModel() instanceof Service) {
71+
$query->setResultSetClass(VolatileCsvResults::class);
72+
} else {
73+
$query->setResultSetClass(__CLASS__);
74+
}
75+
76+
if ($query->hasLimit()) {
77+
// Custom limits should still apply
78+
$query->peekAhead(false);
79+
$offset = $query->getOffset();
80+
} else {
81+
$query->limit(1000);
82+
$query->peekAhead();
83+
$offset = 0;
84+
}
85+
86+
do {
87+
$query->offset($offset);
88+
$result = $query->execute()->disableCache();
89+
foreach ($result as $i => $keysAndValues) {
90+
if ($i === 0) {
91+
echo implode(',', array_keys($keysAndValues));
92+
}
93+
94+
echo "\r\n";
95+
96+
echo implode(',', array_values($keysAndValues));
97+
98+
JsonResultSet::giveMeMoreTime();
99+
}
100+
101+
$offset += 1000;
102+
} while ($result->hasMore());
103+
104+
exit;
105+
}
106+
}

library/Icingadb/Data/JsonResultSet.php

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,77 +4,9 @@
44

55
namespace Icinga\Module\Icingadb\Data;
66

7-
use DateTime;
8-
use DateTimeZone;
9-
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
10-
use Icinga\Util\Json;
11-
use ipl\Orm\Model;
12-
use ipl\Orm\Query;
7+
use ipl\Orm\ResultSet;
138

14-
class JsonResultSet extends VolatileStateResults
9+
class JsonResultSet extends ResultSet
1510
{
16-
protected $isCacheDisabled = true;
17-
18-
/**
19-
* @return array<string, ?string>
20-
*/
21-
public function current(): array
22-
{
23-
return $this->createObject(parent::current());
24-
}
25-
26-
protected function formatValue(string $key, $value): ?string
27-
{
28-
if (
29-
$value
30-
&& (
31-
$key === 'id'
32-
|| substr($key, -3) === '_id'
33-
|| substr($key, -3) === '.id'
34-
|| substr($key, -9) === '_checksum'
35-
|| substr($key, -4) === '_bin'
36-
)
37-
) {
38-
$value = bin2hex($value);
39-
}
40-
41-
if ($value instanceof DateTime) {
42-
return $value->setTimezone(new DateTimeZone('UTC'))
43-
->format('Y-m-d\TH:i:s.vP');
44-
}
45-
46-
return $value;
47-
}
48-
49-
protected function createObject(Model $model): array
50-
{
51-
$keysAndValues = [];
52-
foreach ($model as $key => $value) {
53-
if ($value instanceof Model) {
54-
$keysAndValues[$key] = $this->createObject($value);
55-
} else {
56-
$keysAndValues[$key] = $this->formatValue($key, $value);
57-
}
58-
}
59-
60-
return $keysAndValues;
61-
}
62-
63-
public static function stream(Query $query): void
64-
{
65-
$query->setResultSetClass(__CLASS__);
66-
67-
echo '[';
68-
foreach ($query as $i => $object) {
69-
if ($i > 0) {
70-
echo ",\n";
71-
}
72-
73-
echo Json::sanitize($object);
74-
}
75-
76-
echo ']';
77-
78-
exit;
79-
}
11+
use JsonResultSetUtils;
8012
}

0 commit comments

Comments
 (0)