Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
151 commits
Select commit Hold shift + click to select a range
b11b392
Add: column external_uuid & missing column changed_at
Jan-Schuppik Sep 3, 2025
c7f4f57
Run with:
nilmerg Aug 11, 2025
063701d
Introduce: PSR Logger
Jan-Schuppik Aug 22, 2025
26007b4
Fix: test-setup for MySQL compatibility (PostgreSQL tests still failing)
Jan-Schuppik Aug 14, 2025
c0ed960
Adjust: unit tests
Jan-Schuppik Aug 22, 2025
01b1a6e
Add: route for new api controller
Jan-Schuppik Aug 22, 2025
9cf4925
Introduce: new API srtucture (starting with Contact GET)
Jan-Schuppik Aug 22, 2025
60a3048
Add: initial openapi docs
Jan-Schuppik Aug 22, 2025
556d590
Adjust: give the request object to ApiCore
Jan-Schuppik Aug 22, 2025
5fe5d9e
Fix: lint-errors & moduleName fetching
Jan-Schuppik Aug 25, 2025
68466a6
Remove: Icinga Web 2 BaseTestCase dependency
Jan-Schuppik Aug 25, 2025
b21327f
Adjust: add small review suggestions
Jan-Schuppik Aug 28, 2025
496bf78
Rework: implements psr-15 RequestHandlerInterface
Jan-Schuppik Aug 29, 2025
7d2077c
Adjust: api-version handling
Jan-Schuppik Sep 1, 2025
2f362a8
Fix: request validation & plural-GET result name & test permissions
Jan-Schuppik Sep 1, 2025
7f3f58c
Adjust: openapi documentation creating & storing
Jan-Schuppik Sep 1, 2025
d3c90a3
Fix: Psr-Logger exception-handling
Jan-Schuppik Sep 3, 2025
efdf20c
Introduce: PUT method handling
Jan-Schuppik Sep 3, 2025
aabf00d
Adjust: Openapi Attributes (+ fix strings)
Jan-Schuppik Sep 3, 2025
0ecbe2a
Introduce: POST method handling
Jan-Schuppik Sep 3, 2025
06c5220
Fix: phpdocs of endpoint-methods
Jan-Schuppik Sep 3, 2025
6737e30
Introduce: DELETE method handling
Jan-Schuppik Sep 3, 2025
3c89aa2
WIP: introduce psr15 pipeline & validation middleware
Jan-Schuppik Sep 4, 2025
ec0d8eb
Fix: contact tests (enable POST with identifier)
Jan-Schuppik Sep 4, 2025
126882b
Fix: CodeSniffer Errors
Jan-Schuppik Sep 5, 2025
8e12bf3
Add: contactgroup endpoint methods
Jan-Schuppik Sep 5, 2025
d19a710
Fix: contactgroup tests
Jan-Schuppik Sep 5, 2025
a94806d
Adjust: enriching of get-result-rows
Jan-Schuppik Sep 5, 2025
0ab447a
Add: channel GET-methods
Jan-Schuppik Sep 5, 2025
8a08a07
Fix: wrong Uuid-class & filterString fetching
Jan-Schuppik Sep 5, 2025
29ecb3d
Add: channel tests (+ adjust channel filters)
Jan-Schuppik Sep 5, 2025
c16b06e
Fix: add missing escape-signs in tests
Jan-Schuppik Sep 5, 2025
a3321b4
Fix: CodeSniffer Errors
Jan-Schuppik Sep 5, 2025
45f6920
Remove: error abstraction
Jan-Schuppik Sep 8, 2025
e0a3bae
Adjust: channel-tests add second default channel
Jan-Schuppik Sep 8, 2025
0fc0165
Remove: middleware & middleware-pipeline
Jan-Schuppik Sep 8, 2025
ab85ca0
Fix: channel-tests
Jan-Schuppik Sep 9, 2025
c60a25d
Fix: contact-tests
Jan-Schuppik Sep 10, 2025
1553b51
Fix: contacgroup-tests & contact-tests
Jan-Schuppik Sep 10, 2025
5696b15
Adjust: parse all exceptions in error response (temporary workaround)
Jan-Schuppik Sep 10, 2025
1bc8d5e
Fix: CodeSniffer Errors
Jan-Schuppik Sep 10, 2025
bcdaa6a
Adjust: create response only if necessary
Jan-Schuppik Sep 10, 2025
3df3f44
Fix: general request-body-validation
Jan-Schuppik Sep 10, 2025
2920f41
Adjust: naming of multi-result-response
Jan-Schuppik Sep 11, 2025
59d9a35
Fix: request-body-validation & error-throwing
Jan-Schuppik Sep 11, 2025
acd7420
Adjust: simplify response-creating
Jan-Schuppik Sep 11, 2025
a5e95a3
Adjust: move openapi-logic from core to openapi-endpoint
Jan-Schuppik Sep 11, 2025
8990562
Fix: unit-tests & adjust behavior
Jan-Schuppik Sep 15, 2025
3a4c34a
Adjust: simplify dispatching of ApiController
Jan-Schuppik Sep 18, 2025
beb1697
Adjust: change scope of allowedMethod & generator funcs
Jan-Schuppik Sep 18, 2025
12373f5
Fix: small mistakes in endpoints openapi & contactgroups
Jan-Schuppik Sep 18, 2025
9e34b52
Add: methode to control the creation of a Response
Jan-Schuppik Sep 18, 2025
1f4b181
Adjust: apply review suggestions to tests
Jan-Schuppik Sep 18, 2025
0f7edcd
Adjust: 'expected' variables in test
Jan-Schuppik Sep 18, 2025
ac8fc2d
Adjust: db usage & move general handle-logic to core
Jan-Schuppik Sep 19, 2025
c57b2cb
Remove: http error code 409
Jan-Schuppik Sep 19, 2025
6b52714
Adjust: change endpoint const in abstract method
Jan-Schuppik Sep 19, 2025
7f456b0
Fix: change unclear test names
Jan-Schuppik Sep 19, 2025
0e7e520
Adjust: Http enum creation
Jan-Schuppik Sep 19, 2025
980b2fe
Remove: plural-check from ApiCore and handle it in endpoints
Jan-Schuppik Sep 19, 2025
b92d959
Fix: base assertValidRequest shouldn't provide logic
Jan-Schuppik Sep 19, 2025
624c432
Adjust: beautify filter assembling for plural get
Jan-Schuppik Sep 19, 2025
e77113f
Adjust: json string creation
Jan-Schuppik Sep 19, 2025
180a9c2
Remove: unnecessary stream-request-body method
Jan-Schuppik Sep 19, 2025
dea16fc
Fix: move request asserting to from core to v1
Jan-Schuppik Sep 19, 2025
eb5da26
Adjust: replace PHP-docs with more descriptive test-names
Jan-Schuppik Sep 19, 2025
035e282
Fix: change remaining test-UUID to constants
Jan-Schuppik Sep 22, 2025
69905f0
Adjust: review suggestions
Jan-Schuppik Sep 25, 2025
4ca5e45
Fix: wrong format of endpoint
Jan-Schuppik Sep 25, 2025
03a6143
Fix: mysql external_uuid db type
Jan-Schuppik Sep 25, 2025
5d2552d
Fix: cases of class-call in dispatching
Jan-Schuppik Sep 25, 2025
fe3dd2f
Adjust: response header content-type
Jan-Schuppik Sep 26, 2025
1b1347d
Fix: Code-Sniffer Errors
Jan-Schuppik Sep 26, 2025
9ee787f
Adjust: cleanup row preparation for output
Jan-Schuppik Sep 26, 2025
5cad9b2
Adjust: improve the php-docs
Jan-Schuppik Sep 26, 2025
bbde73d
Adjust: cleanup code & set response-header if not provided
Jan-Schuppik Sep 26, 2025
efbcd6c
Adjust: return response instead of assoc array in endpoint-methods
Jan-Schuppik Sep 29, 2025
cd5f3fe
Fix: change unsuitable names
Jan-Schuppik Sep 29, 2025
22e569a
Fix: correct indentations
Jan-Schuppik Sep 29, 2025
e5208f2
Adjust: use sprintf() to cleanup strings
Jan-Schuppik Sep 29, 2025
1e3c5b5
Remove: TODOs
Jan-Schuppik Sep 29, 2025
b95b58b
Adjust: identifier-check in post-methods
Jan-Schuppik Sep 29, 2025
658959f
Fix: scope of variables initialized in if-condition
Jan-Schuppik Sep 29, 2025
7d4736b
Fix: add spacing to improve readability
Jan-Schuppik Sep 29, 2025
c3067f3
Fix: IDE warnings
Jan-Schuppik Sep 29, 2025
81195be
Adjust: use database via singleton instead of initialize variables
Jan-Schuppik Sep 29, 2025
9c9ace0
Fix: exceeding line
Jan-Schuppik Sep 29, 2025
8c4d74f
Fix: unit-tests for new database-trait
Jan-Schuppik Sep 29, 2025
2dd28b7
Fix: scope of a variable
Jan-Schuppik Sep 30, 2025
276edbb
Adjust: make unit-tests independent
Jan-Schuppik Sep 30, 2025
33cc849
Fix: use getConnection() (need ipl-sql shared-test-databases updated)
Jan-Schuppik Sep 30, 2025
db71dcf
Fix: unit-test errors caused by databases
Jan-Schuppik Oct 1, 2025
5e4c3f4
Revert "Fix: unit-test errors caused by databases"
Jan-Schuppik Oct 2, 2025
a6b655b
Introduce and use new `ApiTestBackends` data provider
nilmerg Oct 2, 2025
8f9cd1a
Fix: unit-tests according to new data provider
Jan-Schuppik Oct 6, 2025
372f77b
Fix: Code-Sniffer Errors
Jan-Schuppik Oct 6, 2025
6e86689
Fix: softdelete-blocks-uuid problem
Jan-Schuppik Oct 7, 2025
6118ca7
php: Spin up database backends for unit tests
nilmerg Oct 8, 2025
14780e9
tests: Set up Icinga Web Database
nilmerg Oct 8, 2025
baa1a97
ApiTestBackends: Pass through `ICINGAWEB_LIBDIR` to webserver
nilmerg Oct 8, 2025
f73db5a
Adjust: implement HandlerInterface in endpoints instead of in base class
Jan-Schuppik Oct 10, 2025
20b4344
Fix: Error-handling, log no-http-errors
Jan-Schuppik Oct 10, 2025
e96da9b
Fix: psr logger
Jan-Schuppik Oct 10, 2025
055b26c
Fix: stream the response
Jan-Schuppik Oct 10, 2025
55be924
Fix: ServerRequest creation, avoid Content-type checking
Jan-Schuppik Oct 14, 2025
f766bf9
Fix: web-forms add column external_uuid
Jan-Schuppik Oct 14, 2025
e9e6ace
Adjust: wrapping-condition of result data in tests
Jan-Schuppik Oct 14, 2025
95ac12d
Fix: HttpMethod enum
Jan-Schuppik Oct 14, 2025
997eb44
Adjust: check if http method exists for endpoint
Jan-Schuppik Oct 14, 2025
9585bc4
Add: license headers
Jan-Schuppik Oct 15, 2025
01c5bfe
Adjust: replace PHP-docs with more descriptive test-names
Jan-Schuppik Sep 19, 2025
42c099d
Introduce: own handling of swagger-php attributes (start with GET)
Jan-Schuppik Sep 24, 2025
e7e780d
Add: DELETE swagger-php attributes
Jan-Schuppik Sep 24, 2025
6202bce
Add: POST swagger-php attributes
Jan-Schuppik Oct 1, 2025
28900b6
Add: PUT swagger-php attributes
Jan-Schuppik Oct 1, 2025
20a155c
WIP: Adjust openapi description according to schemathesis checks
Jan-Schuppik Oct 9, 2025
13b61f7
Adjust: cleanup OpenApi Description
Jan-Schuppik Oct 10, 2025
c832308
Adjust: give the POST request body a new uuid
Jan-Schuppik Oct 15, 2025
fe8bfe7
Fix: standardize the designation of Contact Groups
Jan-Schuppik Oct 15, 2025
784480d
Fix: output format of a single get result
Jan-Schuppik Oct 15, 2025
a99837e
Add: missing license headers (to OAD files)
Jan-Schuppik Oct 15, 2025
b21d269
Fix: Codesniffer errors
Jan-Schuppik Oct 15, 2025
dacd2c9
Fix: rename API directories
Jan-Schuppik Oct 16, 2025
206408f
Introduce: Middleware Pipeline
Jan-Schuppik Oct 16, 2025
e2a1073
tests: Enhance/Cleanup ApiV1ContactsTest
nilmerg Oct 21, 2025
416d28a
tests: Enhance/Cleanup ApiV1ContactGroupsTest
nilmerg Oct 21, 2025
759aadf
tests: Check whether the api updates group memberships properly
nilmerg Oct 21, 2025
3978487
ApiTestBackends: Log to a file instead
nilmerg Oct 21, 2025
6e9ddb5
tests: Check whether the api updates addresses properly
nilmerg Oct 21, 2025
85ab301
tests: Use assertJsonStringEqualsJsonString where applicable
nilmerg Oct 21, 2025
4083ea8
test: Fix codestyle in ApiV1ContactsTest
nilmerg Oct 21, 2025
a3dfd92
Adjust: beautify Middleware & Pipeline
Jan-Schuppik Oct 17, 2025
4d400f0
Fix: updating contactgroup_member via contactgroups-PUT
Jan-Schuppik Oct 17, 2025
6891cb4
Fix: updating contactgroup_member via contacts-PUT
Jan-Schuppik Oct 21, 2025
3b9390c
Fix: OAD headers
Jan-Schuppik Oct 21, 2025
0b23052
Adjust: create OAD via cli-command
Jan-Schuppik Oct 21, 2025
33a9e38
Fix: problems from reviews
Jan-Schuppik Oct 21, 2025
80b45cd
Fix: endpoint behavior according to unit-tests
Jan-Schuppik Oct 21, 2025
bd85f8f
Fix: Codesniffer Errors & Cleanup OpenapiCommand
Jan-Schuppik Oct 27, 2025
09e06ee
Fix: docs REST-API and OAD
Jan-Schuppik Oct 28, 2025
aee2eaa
Fix: middlewares according to reviews
Jan-Schuppik Oct 28, 2025
3cee7e2
Remove: usage of ORM
Jan-Schuppik Oct 28, 2025
1191aed
Fix: tests (typo in classname)
Jan-Schuppik Oct 29, 2025
5d2a744
Adjust: cleanup OAD
Jan-Schuppik Oct 29, 2025
af8d7f5
Revert "Remove: usage of ORM"
Jan-Schuppik Oct 29, 2025
5b66701
Fix: remove unused variable
Jan-Schuppik Oct 29, 2025
9f21561
Adjust: make addresses required & avoid enumerations in messages
Jan-Schuppik Oct 30, 2025
9376563
Add: missing license headers & api-readme overflow prevention
Jan-Schuppik Oct 31, 2025
7bbf13e
Remove: test services
Jan-Schuppik Oct 31, 2025
01a8519
Adjust: get schemas from daemon (wip: from branch with required adjus…
Jan-Schuppik Oct 31, 2025
3db3180
Fix: use main branch of daemon to get schemas
Jan-Schuppik Oct 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,36 @@ jobs:
php: ['8.2', '8.3', '8.4']
os: ['ubuntu-latest']

services:
mysql:
image: mariadb
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: icinga_unittest
MYSQL_USER: icinga_unittest
MYSQL_PASSWORD: icinga_unittest
options: >-
--health-cmd "mariadb -s -uroot -proot -e'SHOW DATABASES;' 2> /dev/null | grep icinga_unittest > test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306/tcp

pgsql:
image: postgres
env:
POSTGRES_USER: icinga_unittest
POSTGRES_PASSWORD: icinga_unittest
POSTGRES_DB: icinga_unittest
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp

steps:
- name: Checkout code base
uses: actions/checkout@v4
Expand All @@ -75,7 +105,36 @@ jobs:
git clone --depth 1 -b snapshot/nightly https://github.com/Icinga/icinga-php-library.git _libraries/ipl
git clone --depth 1 -b snapshot/nightly https://github.com/Icinga/icinga-php-thirdparty.git _libraries/vendor

- name: Checkout Icinga Notifications Daemon
run: |
git clone --depth 1 -b main https://github.com/Icinga/icinga-notifications.git _notifications_daemon

- name: Initialize Icinga Web
run: |
mysql --host="127.0.0.1" --port="${{ job.services.mysql.ports['3306'] }}" --user="root" --password="root" \
-e "CREATE DATABASE icingaweb; CREATE USER icingaweb@'%' IDENTIFIED BY 'icingaweb'; GRANT ALL ON icingaweb.* TO icingaweb@'%';"
PGPASSWORD=icinga_unittest psql --host="127.0.0.1" --port="${{ job.services.pgsql.ports['5432'] }}" \
--username "icinga_unittest" -c "CREATE DATABASE icingaweb;"

- name: PHPUnit
env:
ICINGAWEB_LIBDIR: _libraries
ICINGAWEB_PATH: _icingaweb2
ICINGA_NOTIFICATIONS_SCHEMA: _notifications_daemon/schema
MYSQL_TESTDB: icinga_unittest
MYSQL_TESTDB_HOST: 127.0.0.1
MYSQL_TESTDB_PORT: ${{ job.services.mysql.ports['3306'] }}
MYSQL_TESTDB_USER: icinga_unittest
MYSQL_TESTDB_PASSWORD: icinga_unittest
MYSQL_ICINGAWEBDB: icingaweb
MYSQL_ICINGAWEBDB_PASSWORD: icingaweb
MYSQL_ICINGAWEBDB_USER: icingaweb
PGSQL_TESTDB: icinga_unittest
PGSQL_TESTDB_HOST: 127.0.0.1
PGSQL_TESTDB_PORT: ${{ job.services.pgsql.ports['5432'] }}
PGSQL_TESTDB_USER: icinga_unittest
PGSQL_TESTDB_PASSWORD: icinga_unittest
PGSQL_ICINGAWEBDB: icingaweb
PGSQL_ICINGAWEBDB_PASSWORD: icinga_unittest
PGSQL_ICINGAWEBDB_USER: icinga_unittest
run: phpunit --bootstrap _icingaweb2/test/php/bootstrap.php
157 changes: 157 additions & 0 deletions application/clicommands/OpenapiCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php

/* Icinga Notifications Web | (c) 2025 Icinga GmbH | GPLv2 */

namespace Icinga\Module\Notifications\Clicommands;

use FilesystemIterator;
use Icinga\Application\Icinga;
use Icinga\Cli\Command;
use Icinga\Module\Notifications\Api\OpenApiPreprocessor\AddGlobal401Response;
use Icinga\Module\Notifications\Common\PsrLogger;
use OpenApi\Generator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RuntimeException;
use SplFileInfo;
use Throwable;

class OpenapiCommand extends Command
{
/**
* Generate an OpenAPI JSON file from PHP attributes.
*
* This command scans a directory for PHP files and generates an OpenAPI JSON file using swagger-php.
* The paths are relative to the notifications module directory.
*
* USAGE:
*
* icingacli notifications openapi generate [OPTIONS]
*
* OPTIONS
*
* --dir <path/> Set the path to the directory to scan for PHP files.
* Default: /library/Notifications/Api/
*
* --exclude <comma seperated strings> Exclude files matching these strings. Wildcard is `*`
*
* --include <comma seperated strings> Include files matching these strings. Wildcard is `*`
*
* --output <path/> Set the path to the output file.
* Default: /doc/api/api-v1-public.json
*
* --api-version <version string> Set the API version.
* Default: v1
* If the output path is set the --api-version option is ignored.
*
* --oad-version <version string> Set the OpenAPI version.
* Default: 3.1.0
*/
public function generateAction(): void
{
$directoryInNotifications = $this->params->get('dir', '/library/Notifications/Api/');
$exclude = $this->params->get('exclude');
$include = $this->params->get('include');
$outputPath = $this->params->get('output');
$apiVersion = $this->params->get('api-version', 'v1');
$oadVersion = $this->params->get('oad-version', '3.1.0');

$notificationsPath = Icinga::app()->getModuleManager()->getModule('notifications')->getBaseDir();
$directory = $notificationsPath . $directoryInNotifications;

Check failure on line 60 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Binary operation "." between string and mixed results in an error.

Check failure on line 60 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Binary operation "." between string and mixed results in an error.

Check failure on line 60 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Binary operation "." between string and mixed results in an error.

$baseDirectory = realpath($directory);
if ($baseDirectory === false || ! is_dir($baseDirectory)) {
throw new RuntimeException("Invalid directory: {$directory}");
}

$exclude = isset($exclude) ? array_map('trim', explode(',', $exclude)) : [];

Check failure on line 67 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Parameter #2 $string of function explode expects string, mixed given.

Check failure on line 67 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Parameter #2 $string of function explode expects string, mixed given.

Check failure on line 67 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Parameter #2 $string of function explode expects string, mixed given.
$include = isset($include) ? array_map('trim', explode(',', $include)) : [];

Check failure on line 68 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Parameter #2 $string of function explode expects string, mixed given.

Check failure on line 68 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Parameter #2 $string of function explode expects string, mixed given.

Check failure on line 68 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Parameter #2 $string of function explode expects string, mixed given.
$outputPath = $notificationsPath . ($outputPath ?? '/doc/api/api-' . $apiVersion . '-public.json');

Check failure on line 69 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Binary operation "." between string and mixed results in an error.

Check failure on line 69 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Binary operation "." between '/doc/api/api-' and mixed results in an error.

Check failure on line 69 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Binary operation "." between string and mixed results in an error.

Check failure on line 69 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Binary operation "." between '/doc/api/api-' and mixed results in an error.

Check failure on line 69 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Binary operation "." between string and mixed results in an error.

Check failure on line 69 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Binary operation "." between '/doc/api/api-' and mixed results in an error.

$files = $this->collectPhpFiles($baseDirectory, $exclude, $include);

echo "→ Scanning directory: $baseDirectory\n";
echo "→ Found " . count($files) . " PHP files\n";

$generator = new Generator(new PsrLogger());
$generator->setVersion($oadVersion);

Check failure on line 77 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Parameter #1 $version of method OpenApi\Generator::setVersion() expects string|null, mixed given.

Check failure on line 77 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Parameter #1 $version of method OpenApi\Generator::setVersion() expects string|null, mixed given.

Check failure on line 77 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Parameter #1 $version of method OpenApi\Generator::setVersion() expects string|null, mixed given.
$generator->getProcessorPipeline()->add(new AddGlobal401Response());

try {
$openapi = $generator->generate($files);

$json = $openapi->toJson(

Check failure on line 83 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Cannot call method toJson() on OpenApi\Annotations\OpenApi|null.

Check failure on line 83 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Cannot call method toJson() on OpenApi\Annotations\OpenApi|null.

Check failure on line 83 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Cannot call method toJson() on OpenApi\Annotations\OpenApi|null.
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_IGNORE | JSON_PRETTY_PRINT
);

$dir = dirname($outputPath);
if (! is_dir($dir)) {
mkdir($dir, 0755, true);
}

file_put_contents($outputPath, $json);

echo "OpenAPI documentation written to: $outputPath\n";
} catch (Throwable $e) {
fwrite(STDERR, "Error generating OpenAPI: " . $e->getMessage() . "\n");
exit(1);
}
}

/**
* Recursively scan a directory for PHP files.
*/
protected function collectPhpFiles(string $baseDirectory, array $exclude, array $include): array

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() return type has no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() has parameter $include with no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.3 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() has parameter $exclude with no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() return type has no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() has parameter $include with no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.2 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() has parameter $exclude with no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() return type has no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() has parameter $include with no value type specified in iterable type array.

Check failure on line 104 in application/clicommands/OpenapiCommand.php

View workflow job for this annotation

GitHub Actions / phpstan / Static analysis with phpstan and php 8.4 on ubuntu-latest

Method Icinga\Module\Notifications\Clicommands\OpenapiCommand::collectPhpFiles() has parameter $exclude with no value type specified in iterable type array.
{
$baseDirectory = rtrim($baseDirectory, '/') . '/';
if (! is_dir($baseDirectory)) {
throw new RuntimeException("Directory $baseDirectory does not exist");
}
if (! is_readable($baseDirectory)) {
throw new RuntimeException("Directory $baseDirectory is not readable");
}

$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($baseDirectory, FilesystemIterator::SKIP_DOTS)
);

/** @var SplFileInfo $file */
foreach ($iterator as $file) {
if (! $file->isFile() || $file->getExtension() !== 'php') {
continue;
}

$path = $file->getPathname();

if ($exclude !== [] && $this->matchesAnyPattern($path, $exclude)) {
continue;
}

if ($include !== [] && ! $this->matchesAnyPattern($path, $include)) {
continue;
}

$files[] = $path;
}

if (empty($files)) {
throw new RuntimeException("No PHP files found in $baseDirectory");
}

return $files;
}

protected function matchesAnyPattern(string $string, array $patterns): bool
{
foreach ($patterns as $pattern) {
// Escape regex special chars except for '*'
$regex = '/^' . str_replace('\*', '.*', preg_quote($pattern, '/')) . '$/';
if (preg_match($regex, $string)) {
return true;
}
}

return false;
}
}
76 changes: 76 additions & 0 deletions application/controllers/ApiController.php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing license header. It seems, not only this file is missing it, please make sure all PHP files have one.

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/* Icinga Notifications Web | (c) 2025 Icinga GmbH | GPLv2 */

namespace Icinga\Module\Notifications\Controllers;

use Exception;
use Icinga\Exception\Http\HttpBadRequestException;
use Icinga\Module\Notifications\Api\Middleware\DispatchMiddleware;
use Icinga\Module\Notifications\Api\Middleware\EndpointExecutionMiddleware;
use Icinga\Module\Notifications\Api\Middleware\ErrorHandlingMiddleware;
use Icinga\Module\Notifications\Api\Middleware\LegacyRequestConversionMiddleware;
use Icinga\Module\Notifications\Api\Middleware\MiddlewarePipeline;
use Icinga\Module\Notifications\Api\Middleware\RoutingMiddleware;
use Icinga\Module\Notifications\Api\Middleware\ValidationMiddleware;
use Icinga\Security\SecurityException;
use Icinga\Web\Request;
use ipl\Web\Compat\CompatController;
use Psr\Http\Message\ResponseInterface;

class ApiController extends CompatController
{
/**
* Handle API requests and route them to the appropriate endpoint class.
*
* Processes API requests for the Notifications module, serving as the main entry point for all API interactions.
*
* @return never
* @throws SecurityException
*/
public function indexAction(): never
{
$this->assertPermission('notifications/api');

$pipeline = new MiddlewarePipeline([
new ErrorHandlingMiddleware(),
new LegacyRequestConversionMiddleware($this->getRequest()),
new RoutingMiddleware(),
new DispatchMiddleware(),
new ValidationMiddleware(),
new EndpointExecutionMiddleware(),
]);

$this->emitResponse($pipeline->execute());

exit;
}

/**
* Emit the HTTP response to the client.
*
* @param ResponseInterface $response The response object to emit.
*
* @return void
*/
protected function emitResponse(ResponseInterface $response): void
{
do {
ob_end_clean();
} while (ob_get_level() > 0);

http_response_code($response->getStatusCode());

foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header(sprintf('%s: %s', $name, $value), false);
}
}
header('Content-Type: application/json');

$body = $response->getBody();
while (! $body->eof()) {
echo $body->read(8192);
}
}
}
2 changes: 2 additions & 0 deletions application/forms/ChannelForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use ipl\Validator\EmailAddressValidator;
use ipl\Web\Common\CsrfCounterMeasure;
use ipl\Web\Compat\CompatForm;
use Ramsey\Uuid\Uuid;

/**
* @phpstan-type ChannelOptionConfig array{
Expand Down Expand Up @@ -214,6 +215,7 @@ public function addChannel(): void
$channel = $this->getValues();
$channel['config'] = json_encode($this->filterConfig($channel['config']), JSON_FORCE_OBJECT);
$channel['changed_at'] = (int) (new DateTime())->format("Uv");
$channel['external_uuid'] = Uuid::uuid4()->toString();

$this->db->transaction(function (Connection $db) use ($channel): void {
$db->insert('channel', $channel);
Expand Down
11 changes: 10 additions & 1 deletion application/forms/ContactGroupForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\FormElement\TermInput;
use ipl\Web\FormElement\TermInput\Term;
use Ramsey\Uuid\Uuid;

class ContactGroupForm extends CompatForm
{
Expand Down Expand Up @@ -187,7 +188,15 @@ public function addGroup(): int
$this->db->beginTransaction();

$changedAt = (int) (new DateTime())->format("Uv");
$this->db->insert('contactgroup', ['name' => trim($data['group_name']), 'changed_at' => $changedAt]);

$this->db->insert(
'contactgroup',
[
'name' => trim($data['group_name']),
'changed_at' => $changedAt,
'external_uuid' => Uuid::uuid4()->toString()
]
);

$groupIdentifier = $this->db->lastInsertId();

Expand Down
5 changes: 5 additions & 0 deletions configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
$this->translate('Allow to configure contact groups')
);

$this->providePermission(
'notifications/api',
$this->translate('Allow to modify configuration via API')
);

$this->provideRestriction(
'notifications/filter/objects',
$this->translate('Restrict access to the objects that match the filter')
Expand Down
27 changes: 27 additions & 0 deletions doc/20-REST-API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# REST API

Icinga Notifications Web provides a REST API that allows you to manage notification-related resources programmatically.

With this API, you can:
- Manage **contacts** and **contact groups**
- Read available **notification channels**

This API enables easy integration with external tools, automation workflows, and configuration management systems.

## API Versioning

The API follows a **versioned** structure to ensure backward compatibility and predictable upgrades.

The current and first stable version is: /icingaweb2/notifications/api/v1

Future versions will be accessible under corresponding paths (for example, `/api/v2`), allowing you to migrate at your own pace.

## API Description

The complete API reference for version `v1` is available in [`api/v1.md`](api/v1.md).

It contains an OpenAPI v3.1 description with detailed information about all endpoints, including:
- Request and response schemas
- Example payloads
- Authentication requirements
- Error handling
Loading
Loading