Skip to content

Commit 40d92d5

Browse files
authored
Provide ObjectsRendererHook for icingadb (#192)
requires Icinga/ipl-web#224
2 parents 82bfaf7 + 87721bb commit 40d92d5

File tree

7 files changed

+209
-102
lines changed

7 files changed

+209
-102
lines changed

.github/workflows/phpstan.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ jobs:
1111
with:
1212
dependencies: |
1313
{
14-
"/icingaweb2": "https://github.com/Icinga/icingaweb2.git"
14+
"/icingaweb2" : "https://github.com/Icinga/icingaweb2.git",
15+
"/usr/share/icingaweb2-modules/icingadb" : "https://github.com/Icinga/icingadb-web.git"
1516
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
/* Icinga Notifications Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Notifications\ProvidedHook\Notifications;
6+
7+
use Generator;
8+
use Icinga\Module\Icingadb\Common\Database;
9+
use Icinga\Module\Icingadb\Model\Host;
10+
use Icinga\Module\Icingadb\Model\Service;
11+
use Icinga\Module\Icingadb\Widget\ItemList\HostList;
12+
use Icinga\Module\Icingadb\Widget\ItemList\ServiceList;
13+
use Icinga\Module\Notifications\Hook\ObjectsRendererHook;
14+
use ipl\Html\Attributes;
15+
use ipl\Html\Html;
16+
use ipl\Html\HtmlElement;
17+
use ipl\Html\Text;
18+
use ipl\Html\ValidHtml;
19+
use ipl\Orm\Query;
20+
use ipl\Stdlib\Filter;
21+
use ipl\Web\Widget\StateBall;
22+
23+
class ObjectsRenderer extends ObjectsRendererHook
24+
{
25+
use Database;
26+
27+
public function getHtmlForObjectNames(array $objectIdTags): Generator
28+
{
29+
[$hostsQuery, $servicesQuery] = $this->buildQueries($objectIdTags);
30+
31+
if ($hostsQuery) {
32+
foreach ($hostsQuery as $host) {
33+
$element = new HtmlElement(
34+
'span',
35+
Attributes::create(['class' => 'subject']),
36+
Text::create($host->display_name)
37+
);
38+
39+
yield ['host' => $host->name] => $element;
40+
}
41+
}
42+
43+
if ($servicesQuery) {
44+
$servicesQuery
45+
->with(['state', 'host.state'])
46+
->withColumns(['service.state.soft_state', 'host.state.soft_state']);
47+
48+
foreach ($servicesQuery as $service) {
49+
$hostElm = [
50+
new StateBall($service->host->state->getStateText(), StateBall::SIZE_MEDIUM),
51+
Text::create(' '),
52+
Text::create($service->host->display_name)
53+
];
54+
55+
$serviceElm = new HtmlElement(
56+
'span',
57+
Attributes::create(['class' => 'subject']),
58+
Text::create($service->display_name)
59+
);
60+
61+
$element = Html::sprintf(
62+
t('%s on %s', '<service> on <host>'),
63+
$serviceElm,
64+
new HtmlElement('span', Attributes::create(['class' => 'subject']), ...$hostElm)
65+
);
66+
67+
yield ['host' => $service->host->name, 'service' => $service->name] => $element;
68+
}
69+
}
70+
}
71+
72+
public function getSourceType(): string
73+
{
74+
if (class_exists('Icinga\Module\Icingadb\ProvidedHook\Notifications\ObjectsRenderer')) {
75+
return 'use-icingadb-hook-implementation';
76+
}
77+
78+
return 'icinga2';
79+
}
80+
81+
public function createObjectLink(array $objectIdTag): ?ValidHtml
82+
{
83+
[$hostsQuery, $servicesQuery] = $this->buildQueries([$objectIdTag]);
84+
if ($servicesQuery) {
85+
$serviceStates = $servicesQuery
86+
->columns([])
87+
->with(['state', 'host.state'])
88+
->first();
89+
90+
if ($serviceStates === null) {
91+
return null;
92+
}
93+
94+
return (new ServiceList([$serviceStates]))
95+
->setViewMode('minimal');
96+
}
97+
98+
$hostStates = $hostsQuery
99+
->columns([])
100+
->with('state')
101+
->first();
102+
103+
if ($hostStates === null) {
104+
return null;
105+
}
106+
107+
return (new HostList([$hostStates]))
108+
->setViewMode('minimal');
109+
}
110+
111+
public function getObjectNames(array $objectIdTags): Generator
112+
{
113+
[$hostsQuery, $servicesQuery] = $this->buildQueries($objectIdTags);
114+
115+
if ($hostsQuery) {
116+
foreach ($hostsQuery as $host) {
117+
yield ['host' => $host->name] => $host->display_name;
118+
}
119+
}
120+
121+
if ($servicesQuery) {
122+
foreach ($servicesQuery as $service) {
123+
yield ['host' => $service->host->name, 'service' => $service->name] => sprintf(
124+
t('%s on %s', '<service> on <host>'),
125+
$service->display_name,
126+
$service->host->display_name
127+
);
128+
}
129+
}
130+
}
131+
132+
/**
133+
* Build queries for hosts and services with columns `name` and `display_name`
134+
*
135+
* @param array $objectIdTags
136+
*
137+
* @return Query[]
138+
*/
139+
private function buildQueries(array $objectIdTags): array
140+
{
141+
$filterServices = Filter::any();
142+
$filterHosts = Filter::any();
143+
144+
foreach ($objectIdTags as $tags) {
145+
if (isset($tags['service'])) {
146+
$filterServices->add(
147+
Filter::all(
148+
Filter::equal('service.name', $tags['service']),
149+
Filter::equal('host.name', $tags['host'])
150+
)
151+
);
152+
} else {
153+
$filterHosts->add(Filter::equal('host.name', $tags['host']));
154+
}
155+
}
156+
157+
$hostsQuery = null;
158+
if (! $filterHosts->isEmpty()) {
159+
$hostsQuery = Host::on($this->getDb())
160+
->columns(['name', 'display_name'])
161+
->filter($filterHosts);
162+
}
163+
164+
$servicesQuery = null;
165+
if (! $filterServices->isEmpty()) {
166+
$servicesQuery = Service::on($this->getDb())
167+
->with('host')
168+
->columns([
169+
'service.name',
170+
'service.display_name',
171+
'host.name',
172+
'host.display_name',
173+
])
174+
->filter($filterServices);
175+
}
176+
177+
return [$hostsQuery, $servicesQuery];
178+
}
179+
}

library/Notifications/Widget/ItemList/EventListItem.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use InvalidArgumentException;
1515
use ipl\Html\BaseHtmlElement;
1616
use ipl\Html\Html;
17+
use ipl\Html\HtmlElement;
1718
use ipl\Stdlib\Str;
1819
use ipl\Web\Common\BaseListItem;
1920
use ipl\Web\Widget\Icon;
@@ -103,16 +104,19 @@ protected function assembleTitle(BaseHtmlElement $title): void
103104
$title->addHtml(Html::tag('span', [], sprintf('#%d:', $this->incident->id)));
104105
}
105106

106-
/** @var Objects $obj */
107-
$obj = $this->item->object;
108-
$name = $obj->getName();
109107
if (! $this->list->getNoSubjectLink()) {
110-
$content = new Link($name, Links::event($this->item->id), ['class' => 'subject']);
108+
$content = new Link(null, Links::event($this->item->id));
111109
} else {
112-
$content = Html::tag('span', ['class' => 'subject'], $name);
110+
$content = new HtmlElement('span');
113111
}
114112

115-
$msg = null;
113+
/** @var Objects $obj */
114+
$obj = $this->item->object;
115+
$name = $obj->getName();
116+
117+
$content->addAttributes($name->getAttributes());
118+
$content->addFrom($name);
119+
116120
if ($this->item->severity === null) {
117121
$description = strtolower(trim($this->item->message ?? ''));
118122
if (Str::startsWith($description, 'incident reached age')) {

library/Notifications/Widget/ItemList/IncidentListItem.php

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,19 @@ protected function assembleTitle(BaseHtmlElement $title): void
5858
{
5959
$title->addHtml(Html::tag('span', [], sprintf('#%d:', $this->item->id)));
6060

61-
/** @var Objects $obj */
62-
$obj = $this->item->object;
63-
$name = $obj->getName();
6461
if (! $this->list->getNoSubjectLink()) {
65-
$content = new Link(
66-
$name,
67-
Links::incident($this->item->id),
68-
['class' => 'subject']
69-
);
62+
$content = new Link(null, Links::incident($this->item->id));
7063
} else {
71-
$content = Html::tag(
72-
'span',
73-
['class' => 'subject'],
74-
$name
75-
);
64+
$content = new HtmlElement('span');
7665
}
7766

67+
/** @var Objects $obj */
68+
$obj = $this->item->object;
69+
$name = $obj->getName();
70+
71+
$content->addAttributes($name->getAttributes());
72+
$content->addFrom($name);
73+
7874
$title->addHtml($content);
7975
}
8076

phpstan-baseline-standard.neon

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -230,41 +230,6 @@ parameters:
230230
count: 4
231231
path: application/controllers/ScheduleController.php
232232

233-
-
234-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ScheduleController\\:\\:addAction\\(\\) has no return type specified\\.$#"
235-
count: 1
236-
path: application/controllers/ScheduleController.php
237-
238-
-
239-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ScheduleController\\:\\:addEntryAction\\(\\) has no return type specified\\.$#"
240-
count: 1
241-
path: application/controllers/ScheduleController.php
242-
243-
-
244-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ScheduleController\\:\\:editEntryAction\\(\\) has no return type specified\\.$#"
245-
count: 1
246-
path: application/controllers/ScheduleController.php
247-
248-
-
249-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ScheduleController\\:\\:indexAction\\(\\) has no return type specified\\.$#"
250-
count: 1
251-
path: application/controllers/ScheduleController.php
252-
253-
-
254-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ScheduleController\\:\\:suggestRecipientAction\\(\\) has no return type specified\\.$#"
255-
count: 1
256-
path: application/controllers/ScheduleController.php
257-
258-
-
259-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\SchedulesController\\:\\:indexAction\\(\\) has no return type specified\\.$#"
260-
count: 1
261-
path: application/controllers/SchedulesController.php
262-
263-
-
264-
message: "#^Parameter \\#2 \\$value of static method ipl\\\\Stdlib\\\\Filter\\:\\:equal\\(\\) expects array\\|bool\\|float\\|int\\|string, mixed given\\.$#"
265-
count: 1
266-
path: application/controllers/SchedulesController.php
267-
268233
-
269234
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\AddEscalationForm\\:\\:assemble\\(\\) has no return type specified\\.$#"
270235
count: 1
@@ -875,11 +840,6 @@ parameters:
875840
count: 1
876841
path: library/Notifications/Model/RuleEscalationRecipient.php
877842

878-
-
879-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Schedule\\:\\:createRelations\\(\\) has no return type specified\\.$#"
880-
count: 1
881-
path: library/Notifications/Model/Schedule.php
882-
883843
-
884844
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\ScheduleMember\\:\\:createRelations\\(\\) has no return type specified\\.$#"
885845
count: 1
@@ -1440,46 +1400,6 @@ parameters:
14401400
count: 1
14411401
path: library/Notifications/Widget/ItemList/EventRuleListItem.php
14421402

1443-
-
1444-
message: "#^Cannot access property \\$full_name on mixed\\.$#"
1445-
count: 5
1446-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1447-
1448-
-
1449-
message: "#^Cannot access property \\$name on mixed\\.$#"
1450-
count: 9
1451-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1452-
1453-
-
1454-
message: "#^Cannot access property \\$object on mixed\\.$#"
1455-
count: 2
1456-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1457-
1458-
-
1459-
message: "#^Cannot access property \\$type on mixed\\.$#"
1460-
count: 3
1461-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1462-
1463-
-
1464-
message: "#^Cannot call method getTimestamp\\(\\) on mixed\\.$#"
1465-
count: 1
1466-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1467-
1468-
-
1469-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\ItemList\\\\IncidentHistoryListItem\\:\\:buildMessage\\(\\) should return string but returns mixed\\.$#"
1470-
count: 1
1471-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1472-
1473-
-
1474-
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\ItemList\\\\IncidentHistoryListItem\\:\\:getIncidentEventIcon\\(\\) should return string but returns string\\|null\\.$#"
1475-
count: 1
1476-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1477-
1478-
-
1479-
message: "#^Parameter \\#1 \\$id of static method Icinga\\\\Module\\\\Notifications\\\\Common\\\\Links\\:\\:event\\(\\) expects int, mixed given\\.$#"
1480-
count: 1
1481-
path: library/Notifications/Widget/ItemList/IncidentHistoryListItem.php
1482-
14831403
-
14841404
message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\ItemList\\\\PageSeparatorItem\\:\\:assemble\\(\\) has no return type specified\\.$#"
14851405
count: 1

phpstan.neon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ parameters:
1515

1616
scanDirectories:
1717
- /icingaweb2
18-
- /usr/share/icinga-php/ipl
19-
- /usr/share/icinga-php/vendor
18+
- /usr/share/icinga-php
19+
- /usr/share/icingaweb2-modules
2020

2121
ignoreErrors:
2222
-

run.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
/* Icinga Notifications Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
/** @var $this \Icinga\Application\Modules\Module */
6+
7+
$this->provideHook('Notifications/ObjectsRenderer');

0 commit comments

Comments
 (0)