Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9b982cd
Started on basic page
lachiebol May 3, 2026
69ae322
Clean up css and fix search bar
lachiebol May 3, 2026
0ce4c2e
css theming fixes, adding in designs
lachiebol May 4, 2026
1b6fbf0
prefetched specs and added smooth transitions
lachiebol May 4, 2026
d4251e5
build and cleanup
lachiebol May 4, 2026
9ec2daf
Clean up code and css
lachiebol May 4, 2026
af93984
theming css fixes and dark mode support
lachiebol May 4, 2026
03620be
removed deep linking for now
lachiebol May 4, 2026
2f90532
fix styling
lachiebol May 4, 2026
9c50541
PO suggestions and fixed bug with search deleting elements breaking s…
lachiebol May 5, 2026
b5f4470
codex suggestion to use computed vs method
lachiebol May 5, 2026
1d2769a
remove unused translations and added controller test
lachiebol May 5, 2026
df0a832
Fix vue build
lachiebol May 5, 2026
f5130b2
7.2 support for tests
lachiebol May 5, 2026
af5b4dc
Fix api test
lachiebol May 5, 2026
abd559c
use npm for swagger-ui, add npm script to move swagger files to vue/lib/
lachiebol May 5, 2026
95ccd18
Change swagger to swagger api
lachiebol May 5, 2026
d7c2cd4
remove swagger page title
lachiebol May 5, 2026
cb792d2
fix string
lachiebol May 5, 2026
2293b70
Add auth button in and fix styling
lachiebol May 6, 2026
20758fc
Updated API calls to new endpoint
lachiebol May 6, 2026
46c3cb2
Fix codex review comments
lachiebol May 6, 2026
60735ae
Fix tests
lachiebol May 6, 2026
fbb4e1c
Now loading assets via twig, fixed timeout issues with task, fixed ad…
lachiebol May 7, 2026
2f7fc12
Now using current instance URL for SwaggerUI, to be compatible with c…
lachiebol May 7, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ vendor/**/composer.lock
/vue/dist/*.common.js
/vue/dist/*.map
/vue/dist/*.development.*
/vue/node_modules/
/tmp/specs/*
!/tmp/specs/.gitkeep
/tmp/annotations/*
Expand Down
28 changes: 28 additions & 0 deletions Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/

declare(strict_types=1);

namespace Piwik\Plugins\OpenApiDocs;

use Piwik\Piwik;
use Piwik\View;

class Controller extends \Piwik\Plugin\ControllerAdmin
{
public function swagger(): string
{
Piwik::checkUserHasSomeViewAccess();

$view = new View('@OpenApiDocs/swagger');
$this->setBasicVariablesView($view);

return $view->render();
}
}
29 changes: 29 additions & 0 deletions Menu.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/

namespace Piwik\Plugins\OpenApiDocs;

use Piwik\Menu\MenuAdmin;
use Piwik\Piwik;

class Menu extends \Piwik\Plugin\Menu
{
public function configureAdminMenu(MenuAdmin $menu): void
{
if (!Piwik::hasUserSuperUserAccess()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@lachiebol This should check view access right ?

return;
}

$menu->addPlatformItem(
'OpenApiDocs_SwaggerApi',
$this->urlForAction('swagger'),
30
);
}
}
34 changes: 33 additions & 1 deletion OpenApiDocs.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@ class OpenApiDocs extends \Piwik\Plugin

public function registerEvents()
{
return [];
return [
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
];
}

public function getStylesheetFiles(&$stylesheets): void
{
$stylesheets[] = 'plugins/OpenApiDocs/vue/lib/swagger-ui/swagger-ui.css';
$stylesheets[] = 'plugins/OpenApiDocs/vue/src/SwaggerPage/swagger-ui-overrides.css';
}

public function getJsFiles(&$jsFiles): void
{
$jsFiles[] = 'plugins/OpenApiDocs/vue/lib/swagger-ui/swagger-ui-bundle.js';
}

public function getClientSideTranslationKeys(&$translationKeys): void
{
$translationKeys[] = 'CoreHome_LearnMoreFullStop';
$translationKeys[] = 'OpenApiDocs_ReportingApiMoreInformation';
$translationKeys[] = 'OpenApiDocs_ReportingApiReference';
$translationKeys[] = 'OpenApiDocs_ReportingApiSummary';
$translationKeys[] = 'OpenApiDocs_SwaggerApi';
$translationKeys[] = 'OpenApiDocs_SwaggerPagePluginEmpty';
$translationKeys[] = 'OpenApiDocs_SwaggerPageRequestFailed';
$translationKeys[] = 'OpenApiDocs_SwaggerPageSpecLoadFailed';
$translationKeys[] = 'OpenApiDocs_SwaggerPageSearchNoResults';
$translationKeys[] = 'OpenApiDocs_SwaggerPageSearchPlaceholder';
$translationKeys[] = 'OpenApiDocs_UserAuthentication';
$translationKeys[] = 'OpenApiDocs_UserAuthenticationManageTokens';
$translationKeys[] = 'OpenApiDocs_UserAuthenticationUsingTokenAuth';
}
}
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@

Allow generating OpenAPI documentation for the Matomo public APIs.

## Frontend assets
Swagger UI is managed via npm inside [vue/package.json](/vue/package.json). The plugin runtime does not load Swagger UI from `node_modules`; instead, the needed distributable files are synced into `vue/lib/swagger-ui/`.

Typical workflow:
- Run `npm install` in `plugins/OpenApiDocs/vue`.
- Run `npm run sync-swagger-ui` in `plugins/OpenApiDocs/vue` after adding or updating `swagger-ui-dist`.
- Run `ddev matomo:console vue:build OpenApiDocs` after changing Vue source.

The plugin-specific Swagger overrides live in [vue/src/SwaggerPage/swagger-ui-overrides.css](/vue/src/SwaggerPage/swagger-ui-overrides.css).

## Dependencies
This plugin had its vendored dependencies scoped using [matomo scoper](https://github.com/matomo-org/matomo-scoper). This means that composer packages are prefixed so that they won't conflict with the same libraries used by other plugins.
If you need to update a dependency, you should be able to run `composer install` to populate the vendor directory and then follow the [instructions for scoping a plugin](https://github.com/matomo-org/matomo-scoper#how-to-scope-a-matomo-plugin). Since the scoper.inc.php file already exists, it will hopefully be as simple as running the scoper for this plugin. Once that's done, you'll also need to make some of the dependencies compatible with Matomo's minimum supported version of PHP.
Expand All @@ -30,4 +40,4 @@ return static function (RectorConfig $rectorConfig): void {
```
With all that in place, you should be able to run Rector like so: `vendor/bin/rector process {path_to_this_plugin/vendor/prefixed} --config={path_to_config_file}`

> **_NOTE:_** For Matomo developers, there's an internal DevPluginCommands plugin with a command that handles scoping and running Rector. See the SearchEngineKeywordsPerformance plugin's README.md for more details.
> **_NOTE:_** For Matomo developers, there's an internal DevPluginCommands plugin with a command that handles scoping and running Rector. See the SearchEngineKeywordsPerformance plugin's README.md for more details.
16 changes: 16 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"OpenApiDocs": {
"ReportingApiMoreInformation": "More info about the Matomo APIs available in %1$sIntroduction to Matomo API%2$s and the %3$sMatomo API Reference%4$s.",
"ReportingApiReference": "Reporting API Reference",
"ReportingApiSummary": "All the data in Matomo is available through simple APIs.",
"SwaggerApi": "Swagger API",
"SwaggerPagePluginEmpty": "No plugins are configured in the OpenApiDocs whitelist.",
"SwaggerPageRequestFailed": "The plugin whitelist could not be loaded.",
"SwaggerPageSpecLoadFailed": "The OpenAPI spec could not be loaded for this plugin.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@lachiebol We should also create an FAQ, explaining how you can build the missing spec or it will be auto generated if flag is set, so that user's don't reach out to support after activating the plugin.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@lachiebol We should also create an FAQ, explaining how you can build the missing spec or it will be auto generated if flag is set, so that user's don't reach out to support after activating the plugin.

Would this be a part of the checklist? Adding relevant documentation?

"SwaggerPageSearchNoResults": "No plugins match your search.",
"SwaggerPageSearchPlaceholder": "Search by plugin name",
"UserAuthentication": "User authentication",
"UserAuthenticationManageTokens": "You can manage your authentication tokens on your security page.",
"UserAuthenticationUsingTokenAuth": "If you want to request data within a script, a crontab, etc. you need to add the '%3$s' URL parameter to the API calls for URLs that require authentication."
}
}
7 changes: 7 additions & 0 deletions templates/swagger.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends 'admin.twig' %}

{% set title %}{{ 'OpenApiDocs_SwaggerApi'|translate }}{% endset %}

{% block content %}
<div vue-entry="OpenApiDocs.SwaggerPage"></div>
{% endblock %}
111 changes: 111 additions & 0 deletions tests/Integration/ControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/

declare(strict_types=1);

namespace Piwik\Plugins\OpenApiDocs\tests\Integration;

use Piwik\Access;
use Piwik\Container\StaticContainer;
use Piwik\Plugin\Manager;
use Piwik\Plugins\OpenApiDocs\Controller;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;

/**
* @group OpenApiDocs
* @group ControllerTest
* @group Plugins
*/
class ControllerTest extends IntegrationTestCase
{
/**
* @var Controller
*/
private $controller;

/**
* @var array<string, mixed>
*/
private $backupGet;

/**
* @var array<string, mixed>
*/
private $backupRequest;

public function setUp(): void
{
parent::setUp();

$this->backupGet = $_GET;
$this->backupRequest = $_REQUEST;

Fixture::createSuperUser();
if (!Fixture::siteCreated(1)) {
Fixture::createWebsite('2012-01-01 00:00:00');
}

Fixture::resetTranslations();
Fixture::loadAllTranslations();

Manager::getInstance()->loadPlugin('OpenApiDocs');

$_GET = [
'idSite' => 1,
'period' => 'day',
'date' => 'today',
];
$_REQUEST = $_GET;

$this->controller = new Controller();
}

public function tearDown(): void
{
$_GET = $this->backupGet;
$_REQUEST = $this->backupRequest;

Fixture::resetTranslations();

parent::tearDown();
}

public function testSwaggerRendersAdminPageForViewAccess(): void
{
FakeAccess::clearAccess(
$superUser = false,
$idSitesAdmin = [0],
$idSitesView = [1],
$identity = 'viewAccessUser'
);

$html = $this->controller->swagger();

$this->assertNotSame('', $html);
$this->assertStringContainsString('vue-entry="OpenApiDocs.SwaggerPage"', $html);
$this->assertStringContainsString('Swagger', $html);
}

public function testSwaggerThrowsWhenUserHasNoAccess(): void
{
$originalAccess = StaticContainer::getContainer()->get(Access::class);
StaticContainer::getContainer()->set(Access::class, new FakeAccess(false, [], [], 'noAccess'));

try {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('checkUserHasSomeViewAccess');

$this->controller->swagger();
} finally {
StaticContainer::getContainer()->set(Access::class, $originalAccess);
}
}
}
2 changes: 1 addition & 1 deletion tests/Unit/APITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function testGetOpenApiSpecThrowsExceptionWhenFormatIsInvalid()
$api = $this->buildApiMock('/tmp/CustomAlerts_openapi_spec_v1.0.0.json', true, '{}');

$this->expectException(\Exception::class);
$this->expectExceptionMessage('General_ExceptionInvalidReportRendererFormat');
$this->expectExceptionMessage("Report format 'yaml' not valid");

$api->getOpenApiSpec('CustomAlerts', 'yaml');
}
Expand Down
1 change: 1 addition & 0 deletions vue/dist/OpenApiDocs.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading