Skip to content

Commit 2d1b93e

Browse files
committed
WIP
1 parent 334aaf8 commit 2d1b93e

File tree

7 files changed

+541
-11
lines changed

7 files changed

+541
-11
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/** Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Icingadb\Hook;
6+
7+
use Icinga\Application\Hook;
8+
use Icinga\Application\Logger;
9+
use Icinga\Module\Icingadb\Hook\Common\HookUtils;
10+
use ipl\Orm\Model;
11+
use Throwable;
12+
13+
abstract class CustomVarEnricherHook
14+
{
15+
use HookUtils;
16+
17+
/**
18+
* Return the grouped custom vars
19+
*
20+
* @return array
21+
*/
22+
abstract public function getGroups(): array;
23+
24+
/**
25+
* Return a group name for the given variable name
26+
*
27+
* @param array $vars
28+
*
29+
* @return array
30+
*/
31+
abstract public function enrichCustomVars(array $vars, Model $object): array;
32+
33+
public static function prepareEnrichedCustomVars(array $vars, Model $object): array
34+
{
35+
$enrichedVars = [];
36+
37+
$groups = [];
38+
foreach (Hook::all('Icingadb/CustomVarEnricher') as $hook) {
39+
/** @var self $hook */
40+
try {
41+
$enrichedVars[] = $hook->enrichCustomVars($vars, $object);
42+
$groups[] = $hook->getGroups();
43+
} catch (Throwable $e) {
44+
Logger::error('Failed to load hook %s:', get_class($hook), $e);
45+
}
46+
}
47+
48+
$enrichedVars = array_merge([], ...$enrichedVars);
49+
$groups = array_merge([], ...$groups);
50+
foreach ($vars as $key => $var) {
51+
if (array_key_exists($key, $enrichedVars)) {
52+
$label = key($enrichedVars[$key]);
53+
$vars[$label] = $enrichedVars[$key][$label];
54+
55+
unset($vars[$key]);
56+
57+
$key = $label;
58+
}
59+
60+
foreach ($groups as $group) {
61+
if (array_key_exists($key, $group)) {
62+
unset($vars[$key]);
63+
64+
break;
65+
}
66+
}
67+
}
68+
69+
return [$vars, $groups];
70+
}
71+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
namespace Icinga\Module\Icingadb\ProvidedHook\Icingadb;
4+
5+
use Icinga\Application\Config;
6+
use Icinga\Module\Director\Data\Db\DbConnection;
7+
use Icinga\Module\Director\DataType\DataTypeDatalist;
8+
use Icinga\Module\Director\DataType\DataTypeDictionary;
9+
use Icinga\Module\Director\Db;
10+
use Icinga\Module\Director\Objects\DirectorDatafield;
11+
use Icinga\Module\Director\Objects\IcingaHost;
12+
use Icinga\Module\Director\Objects\IcingaService;
13+
use Icinga\Module\Director\Web\Form\IcingaObjectFieldLoader;
14+
use Icinga\Module\Icingadb\Hook\CustomVarEnricherHook;
15+
use Icinga\Module\Icingadb\Model\Host;
16+
use Icinga\Module\Icingadb\Model\Service;
17+
use ipl\Orm\Model;
18+
19+
class CustomVarEnricher extends CustomVarEnricherHook
20+
{
21+
protected $fieldConfig;
22+
23+
protected $datalistMaps;
24+
25+
protected $groups = [];
26+
27+
public function prefetchForObject(Model $object): bool
28+
{
29+
return false;
30+
}
31+
32+
public function renderCustomVarKey(string $key)
33+
{
34+
return $key;
35+
}
36+
37+
public function renderCustomVarValue(string $key, $value)
38+
{
39+
return $value;
40+
}
41+
42+
public function identifyCustomVarGroup(string $key): ?string
43+
{
44+
return null;
45+
}
46+
47+
public function enrichCustomVars(array $vars, Model $object): array
48+
{
49+
$directorObject = null;
50+
$connection = Db::fromResourceName(Config::module('director')->get('db', 'resource'));
51+
if ($object instanceof Host) {
52+
$directorObject = IcingaHost::load($object->name, $connection);
53+
} elseif ($object instanceof Service) {
54+
$directorHost = IcingaHost::load($object->host->name, $connection);
55+
$directorObject = IcingaService::load(
56+
['object_name' => $object->name, 'host_id' => $directorHost->get('id')],
57+
$connection
58+
);
59+
}
60+
61+
$newVars = [];
62+
$this->fieldConfig = (new IcingaObjectFieldLoader($directorObject))->getFields();
63+
64+
$this->buildDataListMap($connection);
65+
if ($directorObject) {
66+
foreach ($vars as $varName => $customVar) {
67+
$newVars[$varName] = $this->resolveCustomVarMapping($varName, $customVar, $connection);
68+
}
69+
} else {
70+
$newVars = $vars;
71+
}
72+
73+
return $newVars;
74+
}
75+
76+
/**
77+
* Returns the resolved mapping to custom variables in Director
78+
*
79+
* @param string $name
80+
* @param $val
81+
* @param DbConnection $conn
82+
* @param bool $grouping Whether to enable grouping of custom variables into sections
83+
*
84+
* @return array
85+
*/
86+
protected function resolveCustomVarMapping(string $name, $val, DbConnection $conn, bool $grouping = true): array
87+
{
88+
if (isset($this->fieldConfig[$name])) {
89+
/** @var DirectorDatafield $field */
90+
$field = $this->fieldConfig[$name];
91+
$dataType = $field->get('datatype');
92+
93+
if ($dataType === get_class(new DataTypeDictionary())) {
94+
$label = $field->get('caption');
95+
$newVarValue = [];
96+
foreach ($val as $nestedVarName => $nestedVarValue) {
97+
if (isset($this->fieldConfig[$nestedVarName]) && is_array($nestedVarValue)) {
98+
$childValues = [];
99+
foreach ($nestedVarValue as $childName => $childValue) {
100+
$childValues[] = $this->resolveCustomVarMapping($childName, $childValue, $conn, false);
101+
}
102+
103+
$newVarValue[] = [$nestedVarName => array_merge([], ...$childValues)];
104+
} else {
105+
$newVarValue[] = $this->resolveCustomVarMapping(
106+
$nestedVarName,
107+
$nestedVarValue,
108+
$conn,
109+
false
110+
);
111+
}
112+
}
113+
114+
return [$label => array_merge([], ...$newVarValue)];
115+
} elseif ($dataType === get_class(new DataTypeDatalist())) {
116+
if (isset($this->datalistMaps[$name])) {
117+
$val = $this->datalistMaps[$name][$val];
118+
}
119+
120+
$name = $field->get('caption');
121+
} else {
122+
$name = $field->get('caption');
123+
}
124+
125+
if ($grouping && $field->get('category_id') !== null) {
126+
if (! isset($this->groups[$field->getCategoryName()])) {
127+
$this->groups[$field->getCategoryName()] = [$name => $val];
128+
} else {
129+
$this->groups[$field->getCategoryName()][$name] = $val;
130+
}
131+
}
132+
} elseif (is_array($val)) {
133+
$newValue = [];
134+
foreach ($val as $childName => $childValue) {
135+
$newValue[] = $this->resolveCustomVarMapping($childName, $childValue, $conn, false);
136+
}
137+
138+
$val = array_merge([], ...$newValue);
139+
}
140+
141+
return [$name => $val];
142+
}
143+
144+
private function buildDataListMap(DbConnection $db)
145+
{
146+
$fieldsWithDataLists = [];
147+
foreach ($this->fieldConfig as $field) {
148+
if ($field->get('datatype') === 'Icinga\Module\Director\DataType\DataTypeDatalist') {
149+
$fieldsWithDataLists[$field->get('id')] = $field;
150+
}
151+
}
152+
153+
if (! empty($fieldsWithDataLists)) {
154+
$dataListEntries = $db->select()->from(
155+
['dds' => 'director_datafield_setting'],
156+
[
157+
'dds.datafield_id',
158+
'dde.entry_name',
159+
'dde.entry_value'
160+
]
161+
)->join(
162+
['dde' => 'director_datalist_entry'],
163+
'CAST(dds.setting_value AS integer) = dde.list_id',
164+
[]
165+
)->where('dds.datafield_id', array_keys($fieldsWithDataLists))
166+
->where('dds.setting_name', 'datalist_id');
167+
168+
foreach ($dataListEntries as $dataListEntry) {
169+
$field = $fieldsWithDataLists[$dataListEntry->datafield_id];
170+
$this->datalistMaps[$field->get('varname')][$dataListEntry->entry_name] = $dataListEntry->entry_value;
171+
}
172+
}
173+
}
174+
175+
public function getGroups(): array
176+
{
177+
return $this->groups;
178+
}
179+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/** Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Icingadb\Common;
6+
7+
use ipl\Html\BaseHtmlElement;
8+
use ipl\Html\ValidHtml;
9+
10+
class CustomVarItem extends BaseHtmlElement
11+
{
12+
// TODO: Class for an item in custom HTML Section
13+
protected $item;
14+
15+
public function __construct(ValidHtml $item)
16+
{
17+
$this->item = $item;
18+
}
19+
20+
protected function assemble()
21+
{
22+
$this->addHtml($this->item);
23+
}
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/** Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Icingadb\Common;
6+
7+
use ipl\Html\BaseHtmlElement;
8+
9+
class CustomVarSection extends BaseHtmlElement
10+
{
11+
// TODO: Class for custom HTML section
12+
protected $items;
13+
14+
public function addItem(CustomVarItem $item)
15+
{
16+
$this->items[] = $item;
17+
}
18+
19+
public function assemble()
20+
{
21+
$this->addHtml($this->items);
22+
}
23+
}

library/Icingadb/Widget/Detail/CustomVarTable.php

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
namespace Icinga\Module\Icingadb\Widget\Detail;
66

7+
use Icinga\Module\Icingadb\Hook\CustomVarEnricherHook;
78
use Icinga\Module\Icingadb\Hook\CustomVarRendererHook;
89
use ipl\Html\Attributes;
910
use ipl\Html\BaseHtmlElement;
1011
use ipl\Html\Html;
1112
use ipl\Html\HtmlDocument;
1213
use ipl\Html\HtmlElement;
1314
use ipl\Html\Text;
15+
use ipl\Html\ValidHtml;
1416
use ipl\Orm\Model;
1517
use ipl\Web\Widget\EmptyState;
1618
use ipl\Web\Widget\Icon;
@@ -101,11 +103,11 @@ protected function addRow($name, $value)
101103
protected function renderVar($name, $value)
102104
{
103105
if ($this->object !== null && $this->level === 0) {
104-
list($name, $value, $group) = call_user_func($this->hookApplier, $name, $value);
105-
if ($group !== null) {
106-
$this->groups[$group][] = [$name, $value];
107-
return;
108-
}
106+
// list($name, $value, $group) = call_user_func($this->hookApplier, $name, $value);
107+
// if ($group !== null) {
108+
// $this->groups[$group][] = [$name, $value];
109+
// return;
110+
// }
109111
}
110112

111113
$isArray = is_array($value);
@@ -116,6 +118,8 @@ protected function renderVar($name, $value)
116118
case $isArray:
117119
$this->renderObject($name, $value);
118120
break;
121+
case $value instanceof ValidHtml:
122+
// Todo: Needs new implementation
119123
default:
120124
$this->renderScalar($name, $value);
121125
}
@@ -213,9 +217,9 @@ protected function renderGroup(string $name, $entries)
213217

214218
protected function assemble()
215219
{
216-
if ($this->object !== null) {
217-
$this->hookApplier = CustomVarRendererHook::prepareForObject($this->object);
218-
}
220+
// if ($this->object !== null) {
221+
// $this->hookApplier = CustomVarRendererHook::prepareForObject($this->object);
222+
// }
219223

220224
if ($this->headerTitle !== null) {
221225
$this->getAttributes()
@@ -248,20 +252,30 @@ protected function assemble()
248252
ksort($this->data);
249253
}
250254

251-
foreach ($this->data as $name => $value) {
255+
$groups = [];
256+
if ($this->object !== null) {
257+
list($enrichedCustomVars, $groups) = CustomVarEnricherHook::prepareEnrichedCustomVars(
258+
(array) $this->data,
259+
$this->object
260+
);
261+
} else {
262+
$enrichedCustomVars = $this->data;
263+
}
264+
265+
foreach ($enrichedCustomVars as $name => $value) {
252266
$this->renderVar($name, $value);
253267
}
254268

255269
$this->addHtml($this->body);
256270

257271
// Hooks can return objects as replacement for keys, hence a generator is needed for group entries
258272
$genGenerator = function ($entries) {
259-
foreach ($entries as list($key, $value)) {
273+
foreach ($entries as $key => $value) {
260274
yield $key => $value;
261275
}
262276
};
263277

264-
foreach ($this->groups as $group => $entries) {
278+
foreach ($groups as $group => $entries) {
265279
$this->renderGroup($group, $genGenerator($entries));
266280
}
267281
}

run.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
$this->provideHook('ApplicationState');
88
$this->provideHook('X509/Sni');
9+
$this->provideHook('Icingadb/CustomVarEnricher');
910
$this->provideHook('health', 'IcingaHealth');
1011
$this->provideHook('health', 'RedisHealth');
1112
$this->provideHook('Reporting/Report', 'Reporting/HostSlaReport');

0 commit comments

Comments
 (0)