Skip to content

Commit 5cfce34

Browse files
authored
Added endpoint to retrieve static single swagger file, #PG-4593 (#22)
* Added API endpoint to retrieve matomo swagger file * refactored to be more testable and added tests * Changelog updated * Validating format now and added view check * fixed tests * Cleaned up files * Added default param * Updated docs
1 parent 2a89ff8 commit 5cfce34

3 files changed

Lines changed: 165 additions & 1 deletion

File tree

API.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,82 @@
1010
namespace Piwik\Plugins\OpenApiDocs;
1111

1212
use Piwik\Piwik;
13+
use Piwik\Plugin\Manager;
1314
use Piwik\Plugins\OpenApiDocs\Specs\SpecGenerator;
1415

1516
/**
1617
* API for plugin OpenApiDocs
1718
*
19+
* Exposes endpoints to fetch pre-generated OpenAPI specs or generate plugin-specific
20+
* OpenAPI docs on demand.
21+
*
1822
* @method static \Piwik\Plugins\OpenApiDocs\API getInstance()
1923
*/
2024
class API extends \Piwik\Plugin\API
2125
{
26+
/**
27+
* Get the pre-generated single OpenAPI spec file if it exists. This endpoint only reads
28+
* the generated JSON file and does not trigger spec generation.
29+
*
30+
* /index.php?module=API&method=OpenApiDocs.getMatomoOpenApiSpec
31+
*
32+
* @param string $format Output format. Only `json` is supported.
33+
* @return array<string, mixed> The decoded OpenAPI specification payload.
34+
* @throws \Exception If the file is missing, unreadable, or contains invalid JSON.
35+
*/
36+
public function getMatomoOpenApiSpec(string $format = 'json'): array
37+
{
38+
Piwik::checkUserHasSomeViewAccess();
39+
40+
if (strtolower($format) !== 'json') {
41+
throw new \Exception(
42+
Piwik::translate(
43+
'General_ExceptionInvalidReportRendererFormat',
44+
[$format, 'json']
45+
)
46+
);
47+
}
48+
49+
$filePath = $this->getMatomoSpecFilePath();
50+
51+
if (!$this->isSpecFileReadable($filePath)) {
52+
throw new \Exception('OpenAPI spec file was not found. Generate it first via openapidocs:generate-spec-file.');
53+
}
54+
55+
$specContents = $this->readSpecFile($filePath);
56+
if ($specContents === false) {
57+
throw new \Exception('OpenAPI spec file could not be read.');
58+
}
59+
60+
$decodedSpec = json_decode($specContents, true);
61+
if (!is_array($decodedSpec) || json_last_error() !== JSON_ERROR_NONE) {
62+
throw new \Exception('OpenAPI spec file contains invalid JSON.');
63+
}
64+
65+
return $decodedSpec;
66+
}
67+
68+
protected function getMatomoSpecFilePath(): string
69+
{
70+
$currentPluginDir = Manager::getInstance()::getPluginDirectory('OpenApiDocs');
71+
72+
return $currentPluginDir . OpenApiDocs::GENERATED_SPECS_PATH . 'matomo_openapi_spec_v' . OpenApiDocs::DEFAULT_SPEC_VERSION . '.json';
73+
}
74+
75+
protected function isSpecFileReadable(string $filePath): bool
76+
{
77+
return is_file($filePath) && is_readable($filePath);
78+
}
79+
80+
/**
81+
* @param string $filePath
82+
* @return string|false
83+
*/
84+
protected function readSpecFile(string $filePath)
85+
{
86+
return file_get_contents($filePath);
87+
}
88+
2289
/**
2390
* Get the generated API documentation data for the specified plugin.
2491
*

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
## Changelog
22

3-
5.0.2-b1 - 2026-02-16
3+
5.0.2-b1 - 2026-03-16
44
- Added support for string literal union types
5+
- Added API endpoint to retrieve static matomo swagger file
56

67
5.0.1-b1 - 2026-02-16
78
- Added class and function level docs

tests/Unit/APITest.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/**
4+
* Matomo - free/libre analytics platform
5+
*
6+
* @link https://matomo.org
7+
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
namespace Piwik\Plugins\OpenApiDocs\tests\Unit;
13+
14+
require_once PIWIK_INCLUDE_PATH . '/plugins/OpenApiDocs/vendor/autoload.php';
15+
16+
use PHPUnit\Framework\TestCase;
17+
use Piwik\Access;
18+
use Piwik\Container\StaticContainer;
19+
use Piwik\Plugins\OpenApiDocs\API;
20+
use Piwik\Tests\Framework\Mock\FakeAccess;
21+
22+
/**
23+
* @group OpenApiDocs
24+
* @group OpenApiDocs_Unit
25+
* @group OpenApiDocs_APITest
26+
*/
27+
class APITest extends TestCase
28+
{
29+
private $originalAccess;
30+
31+
protected function setUp(): void
32+
{
33+
parent::setUp();
34+
35+
$this->originalAccess = Access::getInstance();
36+
StaticContainer::getContainer()->set(Access::class, new FakeAccess(false, [], [1], 'viewUser'));
37+
}
38+
39+
protected function tearDown(): void
40+
{
41+
StaticContainer::getContainer()->set(Access::class, $this->originalAccess);
42+
43+
parent::tearDown();
44+
}
45+
46+
public function testGetMatomoOpenApiSpecReturnsDecodedJson()
47+
{
48+
$expectedSpec = [
49+
'openapi' => '3.1.0',
50+
'info' => [
51+
'title' => 'Matomo Reporting API',
52+
'version' => '1.0.0',
53+
],
54+
];
55+
56+
$api = $this->buildApiMock(true, json_encode($expectedSpec));
57+
58+
$result = $api->getMatomoOpenApiSpec();
59+
60+
$this->assertSame($expectedSpec, $result);
61+
}
62+
63+
public function testGetMatomoOpenApiSpecThrowsExceptionWhenFileMissing()
64+
{
65+
$api = $this->buildApiMock(false);
66+
67+
$this->expectException(\Exception::class);
68+
$this->expectExceptionMessage('OpenAPI spec file was not found');
69+
70+
$api->getMatomoOpenApiSpec();
71+
}
72+
73+
public function testGetMatomoOpenApiSpecThrowsExceptionWhenJsonIsInvalid()
74+
{
75+
$api = $this->buildApiMock(true, '{invalid json}');
76+
77+
$this->expectException(\Exception::class);
78+
$this->expectExceptionMessage('OpenAPI spec file contains invalid JSON');
79+
80+
$api->getMatomoOpenApiSpec();
81+
}
82+
83+
84+
private function buildApiMock(bool $isReadable, $fileContents = false): API
85+
{
86+
$api = $this->getMockBuilder(API::class)
87+
->onlyMethods(['getMatomoSpecFilePath', 'isSpecFileReadable', 'readSpecFile'])
88+
->getMock();
89+
90+
$api->method('getMatomoSpecFilePath')->willReturn('/tmp/matomo_openapi_spec_v1.0.0.json');
91+
$api->method('isSpecFileReadable')->willReturn($isReadable);
92+
$api->method('readSpecFile')->willReturn($fileContents);
93+
94+
return $api;
95+
}
96+
}

0 commit comments

Comments
 (0)