Skip to content

Commit 1465b7d

Browse files
committed
Support host.vars.* and service.vars.* filters
1 parent 8ed01da commit 1465b7d

File tree

5 files changed

+145
-5
lines changed

5 files changed

+145
-5
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/* Icinga Notifications Web | (c) 2025 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Notifications\Model\Behavior;
6+
7+
use ipl\I18n\Translation;
8+
use ipl\Orm\ColumnDefinition;
9+
use ipl\Orm\Contract\RewriteColumnBehavior;
10+
use ipl\Stdlib\Filter;
11+
12+
/** @internal Temporary implementation */
13+
class IcingaCustomVars implements RewriteColumnBehavior
14+
{
15+
use Translation;
16+
17+
/** @var string */
18+
public const HOST_PREFIX = 'host.vars.';
19+
20+
/** @var string */
21+
public const SERVICE_PREFIX = 'service.vars.';
22+
23+
public function isSelectableColumn(string $name): bool
24+
{
25+
return str_starts_with($name, self::HOST_PREFIX)
26+
|| str_starts_with($name, self::SERVICE_PREFIX);
27+
}
28+
29+
public function rewriteColumn($column, ?string $relation = null)
30+
{
31+
return null;
32+
}
33+
34+
public function rewriteColumnDefinition(ColumnDefinition $def, string $relation): void
35+
{
36+
if (str_starts_with($def->getName(), self::HOST_PREFIX)) {
37+
$def->setLabel(sprintf(
38+
$this->translate(
39+
ucfirst(substr($def->getName(), 0, strpos($def->getName(), '.'))) . ' %s',
40+
'..<customvar-name>'
41+
),
42+
substr($def->getName(), strlen(self::HOST_PREFIX))
43+
));
44+
} elseif (str_starts_with($def->getName(), self::SERVICE_PREFIX)) {
45+
$def->setLabel(sprintf(
46+
$this->translate(
47+
ucfirst(substr($def->getName(), 0, strpos($def->getName(), '.'))) . ' %s',
48+
),
49+
substr($def->getName(), strlen(self::SERVICE_PREFIX))
50+
));
51+
}
52+
}
53+
54+
public function rewriteCondition(Filter\Condition $condition, $relation = null)
55+
{
56+
if (! $this->isSelectableColumn($condition->metaData()->get('columnName', ''))) {
57+
return null;
58+
}
59+
60+
$class = get_class($condition);
61+
62+
return new $class(
63+
$relation . 'object.extra_tag.' . substr($condition->getColumn(), strlen($relation)),
64+
$condition->getValue()
65+
);
66+
}
67+
}

library/Notifications/Model/Event.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use DateTime;
88
use Icinga\Module\Notifications\Common\Database;
99
use Icinga\Module\Notifications\Common\Icons;
10+
use Icinga\Module\Notifications\Model\Behavior\IcingaCustomVars;
1011
use ipl\Orm\Behavior\Binary;
1112
use ipl\Orm\Behavior\BoolCast;
1213
use ipl\Orm\Behavior\MillisecondTimestamp;
@@ -103,6 +104,7 @@ public function createBehaviors(Behaviors $behaviors): void
103104
$behaviors->add(new MillisecondTimestamp(['time']));
104105
$behaviors->add(new Binary(['object_id']));
105106
$behaviors->add(new BoolCast(['mute']));
107+
$behaviors->add(new IcingaCustomVars());
106108
}
107109

108110
public function createRelations(Relations $relations): void

library/Notifications/Model/Incident.php

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

77
use DateTime;
88
use Icinga\Module\Notifications\Common\Database;
9+
use Icinga\Module\Notifications\Model\Behavior\IcingaCustomVars;
910
use ipl\Orm\Behavior\Binary;
1011
use ipl\Orm\Behavior\MillisecondTimestamp;
1112
use ipl\Orm\Behaviors;
@@ -94,6 +95,7 @@ public function createBehaviors(Behaviors $behaviors): void
9495
'started_at',
9596
'recovered_at'
9697
]));
98+
$behaviors->add(new IcingaCustomVars());
9799
}
98100

99101
public function createRelations(Relations $relations): void

library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
use Icinga\Module\Notifications\Common\Auth;
88
use Icinga\Module\Notifications\Common\Database;
9+
use Icinga\Module\Notifications\Model\Behavior\IcingaCustomVars;
910
use Icinga\Module\Notifications\Model\ObjectExtraTag;
1011
use Icinga\Module\Notifications\Model\ObjectIdTag;
1112
use Icinga\Module\Notifications\Util\ObjectSuggestionsCursor;
1213
use ipl\Html\HtmlElement;
14+
use ipl\I18n\Translation;
1315
use ipl\Orm\Exception\InvalidColumnException;
1416
use ipl\Orm\Exception\InvalidRelationException;
1517
use ipl\Orm\Model;
@@ -27,6 +29,7 @@
2729
class ObjectSuggestions extends Suggestions
2830
{
2931
use Auth;
32+
use Translation;
3033

3134
/** @var Model */
3235
protected $model;
@@ -67,7 +70,12 @@ public function getModel(): Model
6770

6871
protected function shouldShowRelationFor(string $column): bool
6972
{
70-
if (strpos($column, '.tag.') !== false || strpos($column, '.extra_tag.') !== false) {
73+
if (
74+
str_contains($column, '.tag.')
75+
|| str_contains($column, '.extra_tag.')
76+
|| str_starts_with($column, IcingaCustomVars::HOST_PREFIX)
77+
|| str_starts_with($column, IcingaCustomVars::SERVICE_PREFIX)
78+
) {
7179
return false;
7280
}
7381

@@ -128,6 +136,16 @@ protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $sea
128136
} elseif (substr($targetPath, -10) === '.extra_tag') {
129137
$isTag = true;
130138
$targetPath = substr($targetPath, 0, -9) . 'object_extra_tag';
139+
} elseif (
140+
str_starts_with($column, IcingaCustomVars::HOST_PREFIX)
141+
|| str_starts_with($column, IcingaCustomVars::SERVICE_PREFIX)
142+
) {
143+
$isTag = true;
144+
$columnName = $column;
145+
$targetPath = $query->getResolver()->qualifyPath(
146+
'object.object_extra_tag',
147+
$model->getTableName()
148+
);
131149
}
132150

133151
if (strpos($targetPath, '.') !== false) {
@@ -183,6 +201,8 @@ protected function fetchColumnSuggestions($searchTerm)
183201
yield $columnName => $columnMeta;
184202
}
185203

204+
$parsedArrayVars = [];
205+
186206
// Custom variables only after the columns are exhausted and there's actually a chance the user sees them
187207
foreach ([new ObjectIdTag(), new ObjectExtraTag()] as $model) {
188208
$titleAdded = false;
@@ -199,9 +219,34 @@ protected function fetchColumnSuggestions($searchTerm)
199219
));
200220
}
201221

202-
$relation = $isIdTag ? 'object.tag' : 'object.extra_tag';
203-
204-
yield $relation . '.' . $tag->tag => ucfirst($tag->tag);
222+
if (
223+
str_starts_with($tag->tag, IcingaCustomVars::HOST_PREFIX)
224+
|| str_starts_with($tag->tag, IcingaCustomVars::SERVICE_PREFIX)
225+
) {
226+
$search = $name = $tag->tag;
227+
if (preg_match('/\w+(?:\[(\d*)])+$/', $name, $matches)) {
228+
$name = substr($name, 0, -(strlen($matches[1]) + 2));
229+
if (isset($parsedArrayVars[$name])) {
230+
continue;
231+
}
232+
233+
$parsedArrayVars[$name] = true;
234+
$search = $name . '[*]';
235+
}
236+
237+
yield $search => sprintf($this->translate(
238+
$this->translate(ucfirst(substr($name, 0, strpos($name, '.')))) . ' %s',
239+
'..<customvar-name>'
240+
), substr($name, strlen(
241+
str_starts_with($name, IcingaCustomVars::HOST_PREFIX)
242+
? IcingaCustomVars::HOST_PREFIX
243+
: IcingaCustomVars::SERVICE_PREFIX
244+
)));
245+
} else {
246+
$relation = $isIdTag ? 'object.tag' : 'object.extra_tag';
247+
248+
yield $relation . '.' . $tag->tag => ucfirst($tag->tag);
249+
}
205250
}
206251
}
207252
}

library/Notifications/Widget/Detail/IncidentDetail.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Icinga\Module\Notifications\Widget\Detail;
66

77
use Icinga\Module\Notifications\Hook\ObjectsRendererHook;
8+
use Icinga\Module\Notifications\Model\Behavior\IcingaCustomVars;
89
use Icinga\Module\Notifications\Model\Incident;
910
use Icinga\Module\Notifications\View\IncidentContactRenderer;
1011
use Icinga\Module\Notifications\View\IncidentHistoryRenderer;
@@ -16,11 +17,14 @@
1617
use ipl\Html\HtmlElement;
1718
use ipl\Html\Table;
1819
use ipl\Html\Text;
20+
use ipl\I18n\Translation;
1921
use ipl\Stdlib\Filter;
2022
use ipl\Web\Layout\MinimalItemLayout;
2123

2224
class IncidentDetail extends BaseHtmlElement
2325
{
26+
use Translation;
27+
2428
/** @var Incident */
2529
protected $incident;
2630

@@ -107,7 +111,27 @@ protected function createObjectTag(): array
107111
{
108112
$tags = [];
109113
foreach ($this->incident->object->object_extra_tag as $extraTag) {
110-
$tags[] = Table::row([$extraTag->tag, $extraTag->value]);
114+
if (
115+
str_starts_with($extraTag->tag, IcingaCustomVars::HOST_PREFIX)
116+
|| str_starts_with($extraTag->tag, IcingaCustomVars::SERVICE_PREFIX)
117+
) {
118+
$tags[] = Table::row([
119+
sprintf(
120+
$this->translate(
121+
ucfirst(substr($extraTag->tag, 0, strpos($extraTag->tag, '.'))) . ' %s',
122+
'..<customvar-name>'
123+
),
124+
substr($extraTag->tag, strlen(
125+
str_starts_with($extraTag->tag, IcingaCustomVars::HOST_PREFIX)
126+
? IcingaCustomVars::HOST_PREFIX
127+
: IcingaCustomVars::SERVICE_PREFIX
128+
))
129+
),
130+
$extraTag->value
131+
]);
132+
} else {
133+
$tags[] = Table::row([$extraTag->tag, $extraTag->value]);
134+
}
111135
}
112136

113137
if (! $tags) {

0 commit comments

Comments
 (0)