Skip to content

Commit 9034db3

Browse files
wip
1 parent 0b0269a commit 9034db3

File tree

2 files changed

+759
-0
lines changed

2 files changed

+759
-0
lines changed
Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
<?php
2+
3+
/* Icinga Notifications Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Notifications\Controllers;
6+
7+
use Icinga\Exception\Http\HttpException;
8+
use Icinga\Module\Notifications\Common\Database;
9+
use Icinga\Util\Environment;
10+
use Icinga\Util\Json;
11+
use ipl\Sql\Compat\FilterProcessor;
12+
use ipl\Sql\Select;
13+
use ipl\Web\Compat\CompatController;
14+
use ipl\Web\Filter\QueryString;
15+
use ipl\Web\Url;
16+
17+
class ApiV1ContactgroupsController extends CompatController
18+
{
19+
private const ENDPOINT = 'notifications/api/v1/contactgroups';
20+
21+
/**
22+
* @return void
23+
*/
24+
public function indexAction(): void
25+
{
26+
$this->assertPermission('notifications/api/v1');
27+
28+
$request = $this->getRequest();
29+
if (! $request->isApiRequest()) {
30+
$this->httpBadRequest('No API request');
31+
}
32+
33+
$method = $request->getMethod();
34+
if (in_array($method, ['POST', 'PUT'])
35+
&& (! preg_match('/([^;]*);?/', $request->getHeader('Content-Type'), $matches)
36+
|| $matches[1] !== 'application/json'
37+
)
38+
) {
39+
$this->httpBadRequest('No JSON content');
40+
}
41+
42+
$results = [];
43+
$responseCode = 200;
44+
$db = Database::get();
45+
$identifier = $request->getParam('identifier');
46+
// TODO: Remove rawurldecode(). Only added to test, bcz phpstorm's http client encodes the params
47+
$queryString = rawurldecode(Url::fromRequest()->getQueryString());
48+
$filter = FilterProcessor::assembleFilter(QueryString::parse($queryString));
49+
50+
switch ($method) {
51+
case 'GET':
52+
$stmt = (new Select())
53+
->distinct()
54+
->from('contactgroup cg')
55+
->columns([
56+
'contactgroup_id' => 'cg.id',
57+
'id' => 'cg.external_uuid',
58+
'name'
59+
]);
60+
61+
if ($identifier !== null) {
62+
$stmt->where(['external_uuid = ?' => $identifier]);
63+
$result = $db->fetchOne($stmt);
64+
65+
$users = $this->fetchUserIdentifiers($result->contactgroup_id);
66+
if ($users) {
67+
$result->users = $users;
68+
}
69+
70+
unset($result->contactgroup_id);
71+
$results[] = $result;
72+
73+
break;
74+
}
75+
76+
if ($filter !== null) {
77+
$stmt->where($filter);
78+
}
79+
80+
$stmt->limit(500);
81+
$offset = 0;
82+
83+
ob_end_clean();
84+
Environment::raiseExecutionTime();
85+
86+
$this->getResponse()
87+
->setHeader('Content-Type', 'application/json')
88+
->setHeader('Cache-Control', 'no-store')
89+
->sendResponse();
90+
91+
echo '[';
92+
93+
$res = $db->select($stmt->offset($offset));
94+
do {
95+
foreach ($res as $i => $row) {
96+
$users = $this->fetchUserIdentifiers($row->contactgroup_id);
97+
if ($users) {
98+
$row->users = $users;
99+
}
100+
101+
if ($i > 0 || $offset !== 0) {
102+
echo ",\n";
103+
}
104+
105+
unset($row->contactgroup_id);
106+
107+
echo Json::sanitize($row);
108+
}
109+
110+
$offset += 500;
111+
$res = $db->select($stmt->offset($offset));
112+
} while ($res->rowCount());
113+
114+
echo ']';
115+
116+
exit;
117+
case 'POST':
118+
if ($filter !== null) {
119+
$this->httpBadRequest('Cannot filter on POST');
120+
}
121+
122+
$data = $request->getPost();
123+
124+
if (! isset($data['id'], $data['name'])) {
125+
$this->httpBadRequest('missing required fields');
126+
}
127+
128+
$db->beginTransaction();
129+
if ($identifier === null) {
130+
$identifier = $data['id'];
131+
132+
if ($this->getContactgroupId($identifier) !== null) {
133+
throw new HttpException('422', 'Contactgroup already exists');
134+
}
135+
136+
$this->addContactgroup($data);
137+
} else {
138+
$contactgroupId = $this->getContactgroupId($identifier);
139+
if ($contactgroupId === null) {
140+
$this->httpNotFound('Contactgroup not found');
141+
}
142+
143+
if ($identifier === $data['id']) {
144+
throw new HttpException('422', 'Contactgroup already exists');
145+
}
146+
147+
$identifier = $data['id'];
148+
$this->removeContactgroup($contactgroupId);
149+
$this->addContactgroup($data);
150+
}
151+
152+
$db->commitTransaction();
153+
154+
$this->getResponse()->setHeader('Location', self::ENDPOINT . '/' . $identifier);
155+
$responseCode = 201;
156+
157+
break;
158+
case 'PUT':
159+
if ($identifier === null) {
160+
$this->httpBadRequest('Identifier is required');
161+
}
162+
163+
$data = $request->getPost();
164+
165+
if (! isset($data['id'], $data['name'])) {
166+
$this->httpBadRequest('missing required fields');
167+
}
168+
169+
if ($identifier !== $data['id']) {
170+
$this->httpBadRequest('Identifier mismatch');
171+
}
172+
173+
$db->beginTransaction();
174+
$contactgroupId = $this->getContactgroupId($identifier);
175+
if ($contactgroupId !== null) {
176+
$db->update('contactgroup', [
177+
'name' => $data['name'],
178+
], ['id = ?' => $identifier]);
179+
180+
if (! empty($data['users'])) {
181+
$db->delete('contactgroup_member', ['contactgroup_id = ?' => $identifier]);
182+
183+
$this->assertUsersExist($data['users']);
184+
185+
foreach ($data['users'] as $userId) {
186+
$db->insert('contactgroup_member', [
187+
'contactgroup_id' => $identifier,
188+
'contact_id' => $userId
189+
]);
190+
}
191+
}
192+
193+
$responseCode = 204;
194+
} else {
195+
$this->addContactgroup($data);
196+
$responseCode = 201;
197+
}
198+
199+
$db->commitTransaction();
200+
201+
break;
202+
case 'DELETE':
203+
if ($identifier === null) {
204+
$this->httpBadRequest('Identifier is required');
205+
}
206+
207+
$db->beginTransaction();
208+
209+
$contactgroupId = $this->getContactgroupId($identifier);
210+
if ($contactgroupId === null) {
211+
$this->httpNotFound('Contactgroup not found');
212+
}
213+
214+
$this->removeContactgroup($contactgroupId);
215+
216+
$db->commitTransaction();
217+
218+
$responseCode = 204;
219+
220+
break;
221+
default:
222+
$this->httpBadRequest('Invalid method');
223+
}
224+
225+
$this->getResponse()
226+
->setHttpResponseCode($responseCode)
227+
->json()
228+
->setSuccessData($results)
229+
->sendResponse();
230+
}
231+
232+
/**
233+
* Fetch the user(contact) identifiers of the contactgroup with the given id
234+
*
235+
* @param int $contactgroupId
236+
*
237+
* @return ?string[]
238+
*/
239+
private function fetchUserIdentifiers(int $contactgroupId): ?array
240+
{
241+
$users = Database::get()->fetchCol(
242+
(new Select())
243+
->from('contactgroup_member cgm')
244+
->columns('co.external_uuid')
245+
->joinLeft('contact co', 'co.id = cgm.contactgroup_id')
246+
->where(['cgm.contactgroup_id = ?' => $contactgroupId])
247+
->groupBy('co.external_uuid')
248+
);
249+
250+
return ! empty($users) ? $users : null;
251+
}
252+
253+
/**
254+
* Assert that the given user IDs exist
255+
*
256+
* @param array $userIds
257+
*
258+
* @throws HttpException if a group does not exist
259+
*/
260+
private function assertUsersExist(array $userIds): void
261+
{
262+
$existingUserIds = Database::get()->fetchCol(
263+
(new Select())
264+
->from('contact')
265+
->columns('id')
266+
->where(['id IN (?)' => $userIds])
267+
);
268+
269+
if (count($existingUserIds) !== count($userIds)) {
270+
throw new HttpException('404', 'Undefined user identifier given');
271+
}
272+
}
273+
274+
/**
275+
* Get the contactgroup id with the given identifier
276+
*
277+
* @param string $identifier
278+
*
279+
* @return ?int Returns null, if contact does not exist
280+
*/
281+
private function getContactgroupId(string $identifier): ?int
282+
{
283+
$contactgroup = Database::get()->fetchOne(
284+
(new Select())
285+
->from('contactgroup')
286+
->columns('id')
287+
->where(['external_uuid = ?' => $identifier])
288+
);
289+
290+
return $contactgroup->id ?? null;
291+
}
292+
293+
/**
294+
* Add a new contactgroup with the given data
295+
*
296+
* @param array $data
297+
*
298+
* @throws HttpException if a user does not exist
299+
*/
300+
private function addContactgroup(array $data): void
301+
{
302+
$db = Database::get();
303+
$db->insert('contactgroup', [
304+
'name' => $data['name'],
305+
'external_uuid' => $data['id']
306+
]);
307+
308+
$id = $db->lastInsertId();
309+
310+
if (! empty($data['users'])) {
311+
$this->assertUsersExist($data['users']);
312+
foreach ($data['users'] as $userId) {
313+
$db->insert('contactgroup_member', [
314+
'contactgroup_id' => $id,
315+
'contact_id' => $userId
316+
]);
317+
}
318+
}
319+
}
320+
321+
/**
322+
* Remove the contactgroup with the given id
323+
*
324+
* @param int $id
325+
*/
326+
private function removeContactgroup(int $id): void
327+
{
328+
$db = Database::get();
329+
330+
$db->delete('contactgroup_member', ['contactgroup_id = ?' => $id]);
331+
$db->delete('contactgroup', ['id = ?' => $id]);
332+
}
333+
}

0 commit comments

Comments
 (0)