Skip to content

Commit f290b8f

Browse files
authored
Added new endpoint to pull class level descriptions, #PG-5184 (#37)
* Added new endpoint to pull class level descriptions as well * Handle getPluginDescription errors * Make method more generic
1 parent 41aa12f commit f290b8f

4 files changed

Lines changed: 127 additions & 0 deletions

File tree

API.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ public function getAllowedPlugins(): array
3737
return $this->getPluginListProvider()->getAllowedPlugins();
3838
}
3939

40+
/**
41+
* Returns metadata for the plugins used by OpenApiDocs spec generation.
42+
*
43+
* @return array<string, array{description: string}>
44+
*/
45+
public function getAllowedPluginMetadata(): array
46+
{
47+
Piwik::checkUserHasSomeViewAccess();
48+
49+
return $this->getPluginListProvider()->getAllowedPluginMetadata();
50+
}
51+
4052
/**
4153
* Returns a previously generated OpenAPI specification for a plugin.
4254
*

Generation/PluginListProvider.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Piwik\Plugins\OpenApiDocs\Generation;
1313

14+
use Piwik\API\Proxy;
15+
use Piwik\API\Request;
1416
use Piwik\Piwik;
1517
use Piwik\Plugin\Manager;
1618

@@ -42,6 +44,28 @@ public function getAllowedPlugins(): array
4244
}));
4345
}
4446

47+
/**
48+
* @return array<string, array{description: string}>
49+
*/
50+
public function getAllowedPluginMetadata(): array
51+
{
52+
$metadata = [];
53+
54+
foreach ($this->getAllowedPlugins() as $pluginName) {
55+
try {
56+
$description = $this->getPluginDescription($pluginName);
57+
} catch (\Throwable $e) {
58+
$description = '';
59+
}
60+
61+
$metadata[$pluginName] = [
62+
'description' => $description,
63+
];
64+
}
65+
66+
return $metadata;
67+
}
68+
4569
private function shouldIncludeEventProvidedPlugin(string $pluginName): bool
4670
{
4771
if (!$this->pluginManager->isPluginInFilesystem($pluginName)) {
@@ -55,6 +79,15 @@ protected function pluginHasApiFile(string $pluginName): bool
5579
return is_file(Manager::getPluginDirectory($pluginName) . '/API.php');
5680
}
5781

82+
protected function getPluginDescription(string $pluginName): string
83+
{
84+
$apiClassName = Request::getClassNameAPI($pluginName);
85+
Proxy::getInstance()->registerClass($apiClassName);
86+
$documentation = Proxy::getInstance()->getMetadata()[$apiClassName]['__documentation'] ?? '';
87+
88+
return is_string($documentation) ? trim(strip_tags($documentation)) : '';
89+
}
90+
5891
/**
5992
* @param string[] $pluginNames
6093
*/

tests/Unit/APITest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ public function testGetAllowedPluginsReturnsProviderValues(): void
7777
$this->assertSame(['Login', 'ActivityLog'], $api->getAllowedPlugins());
7878
}
7979

80+
public function testGetAllowedPluginMetadataReturnsProviderValues(): void
81+
{
82+
$provider = $this->createMock(PluginListProvider::class);
83+
$provider->expects($this->once())
84+
->method('getAllowedPluginMetadata')
85+
->willReturn([
86+
'Login' => ['description' => 'Login API description'],
87+
'ActivityLog' => ['description' => ''],
88+
]);
89+
90+
$api = $this->getMockBuilder(API::class)
91+
->onlyMethods(['getPluginListProvider'])
92+
->getMock();
93+
$api->method('getPluginListProvider')->willReturn($provider);
94+
95+
$this->assertSame([
96+
'Login' => ['description' => 'Login API description'],
97+
'ActivityLog' => ['description' => ''],
98+
], $api->getAllowedPluginMetadata());
99+
}
100+
80101
public function testGetOpenApiSpecThrowsExceptionWhenFileMissing()
81102
{
82103
$api = $this->buildApiMock('/tmp/CustomAlerts_openapi_spec_v1.0.0.json', false);

tests/Unit/Generation/PluginListProviderTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,67 @@ static function (string $eventName, array $params): void {
107107
$this->assertSame(['HasApi', 'Login', 'InactivePlugin'], $provider->getAllowedPlugins());
108108
}
109109

110+
public function testGetAllowedPluginMetadataReturnsDescriptionPerAllowedPlugin(): void
111+
{
112+
$provider = $this->getMockBuilder(PluginListProvider::class)
113+
->disableOriginalConstructor()
114+
->onlyMethods(['getAllowedPlugins', 'getPluginDescription'])
115+
->getMock();
116+
117+
$provider->method('getAllowedPlugins')
118+
->willReturn(['Login', 'ActivityLog']);
119+
$provider->method('getPluginDescription')
120+
->willReturnMap([
121+
['Login', 'Login API description'],
122+
['ActivityLog', 'Activity log API description'],
123+
]);
124+
125+
$this->assertSame([
126+
'Login' => ['description' => 'Login API description'],
127+
'ActivityLog' => ['description' => 'Activity log API description'],
128+
], $provider->getAllowedPluginMetadata());
129+
}
130+
131+
public function testGetAllowedPluginMetadataReturnsEmptyStringWhenDescriptionMissing(): void
132+
{
133+
$provider = $this->getMockBuilder(PluginListProvider::class)
134+
->disableOriginalConstructor()
135+
->onlyMethods(['getAllowedPlugins', 'getPluginDescription'])
136+
->getMock();
137+
138+
$provider->method('getAllowedPlugins')
139+
->willReturn(['Login']);
140+
$provider->method('getPluginDescription')
141+
->willReturn('');
142+
143+
$this->assertSame(['Login' => ['description' => '']], $provider->getAllowedPluginMetadata());
144+
}
145+
146+
public function testGetAllowedPluginMetadataContinuesWhenOnePluginDescriptionFails(): void
147+
{
148+
$provider = $this->getMockBuilder(PluginListProvider::class)
149+
->disableOriginalConstructor()
150+
->onlyMethods(['getAllowedPlugins', 'getPluginDescription'])
151+
->getMock();
152+
153+
$provider->method('getAllowedPlugins')
154+
->willReturn(['Login', 'BrokenPlugin', 'ActivityLog']);
155+
$provider->method('getPluginDescription')
156+
->willReturnCallback(static function (string $pluginName): string {
157+
if ($pluginName === 'BrokenPlugin') {
158+
throw new \RuntimeException('Could not register API class');
159+
}
160+
161+
return $pluginName === 'Login' ? 'Login API description' : 'Activity log API description';
162+
});
163+
164+
$this->assertSame([
165+
'Login' => ['description' => 'Login API description'],
166+
'BrokenPlugin' => ['description' => ''],
167+
'ActivityLog' => ['description' => 'Activity log API description'],
168+
], $provider->getAllowedPluginMetadata());
169+
}
170+
110171
/**
111172
* @param string[] $activatedPlugins
112173
* @param array<string, bool> $inFilesystemByPlugin

0 commit comments

Comments
 (0)