Skip to content

Commit dc6e0e7

Browse files
committed
Moved API documentation from hard coded paths to a resolver service, if on Cloud, it will use CloudDistributedCachePath, else, it will use /tmp in the plugin
1 parent 6ae5e76 commit dc6e0e7

8 files changed

Lines changed: 209 additions & 25 deletions

API.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Piwik\Piwik;
1313
use Piwik\Plugin\Manager;
1414
use Piwik\Plugins\OpenApiDocs\Specs\SpecGenerator;
15+
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
1516

1617
/**
1718
* API for plugin OpenApiDocs
@@ -67,9 +68,7 @@ public function getOpenApiSpec(string $pluginName, string $format = 'json'): arr
6768

6869
protected function getSpecFilePath(string $pluginName): string
6970
{
70-
$currentPluginDir = Manager::getInstance()::getPluginDirectory('OpenApiDocs');
71-
72-
return $currentPluginDir . OpenApiDocs::GENERATED_SPECS_PATH . $pluginName . '_openapi_spec_v' . OpenApiDocs::DEFAULT_SPEC_VERSION . '.json';
71+
return $this->getSpecPathResolver()->getSpecFilePath($pluginName);
7372
}
7473

7574
protected function isSpecFileReadable(string $filePath): bool
@@ -98,6 +97,11 @@ protected function validateJsonFormat(string $format): void
9897
}
9998
}
10099

100+
protected function getSpecPathResolver(): PathResolver
101+
{
102+
return new PathResolver();
103+
}
104+
101105
/**
102106
* Get the generated API documentation data for the specified plugin.
103107
*

Annotations/AnnotationGenerator.php

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
use Piwik\API\NoDefaultValue;
2121
use Piwik\API\Proxy;
2222
use Piwik\API\Request;
23+
use Piwik\Filesystem;
2324
use Piwik\Http;
2425
use Piwik\Piwik;
2526
use Piwik\Plugin\Manager;
2627
use Piwik\Plugins\OpenApiDocs\OpenApiDocs;
28+
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
2729
use Piwik\SettingsPiwik;
2830
use Piwik\Url;
2931
use Piwik\UrlHelper;
@@ -72,6 +74,11 @@ class AnnotationGenerator
7274
*/
7375
protected $generator;
7476

77+
/**
78+
* @var PathResolver
79+
*/
80+
protected $pathResolver;
81+
7582
/**
7683
* @var array[]
7784
*/
@@ -82,9 +89,10 @@ class AnnotationGenerator
8289
*/
8390
protected $missingImportantDataWarnings;
8491

85-
public function __construct(DocumentationGenerator $generator)
92+
public function __construct(DocumentationGenerator $generator, ?PathResolver $pathResolver = null)
8693
{
8794
$this->generator = $generator;
95+
$this->pathResolver = $pathResolver ?? new PathResolver();
8896
$this->missingImportantDataWarnings = [];
8997
$this->currentPluginDir = Manager::getInstance()::getPluginDirectory('OpenApiDocs');
9098
}
@@ -115,8 +123,7 @@ public function generatePluginApiAnnotations(string $pluginName, bool $writeToFi
115123
}
116124

117125
$rules = require $this->currentPluginDir . '/Annotations/config.php';
118-
$pluginAnnotationDir = $this->currentPluginDir . OpenApiDocs::GENERATED_ANNOTATIONS_PATH;
119-
$pluginAnnotationPath = $pluginAnnotationDir . "/{$pluginName}GeneratedAnnotations.php";
126+
$pluginAnnotationPath = $this->pathResolver->getAnnotationFilePath($pluginName);
120127

121128
$className = Request::getClassNameAPI($pluginName);
122129

@@ -231,8 +238,7 @@ public function getContentForGeneratedAnnotationsFile(array $annotations, string
231238
*/
232239
protected function writeAnnotationsToFile(array $annotations, string $filePath, string $pluginName)
233240
{
234-
// Create or overwrite the annotations file
235-
return file_put_contents($filePath, $this->getContentForGeneratedAnnotationsFile($annotations, $pluginName));
241+
return $this->writeFile($filePath, $this->getContentForGeneratedAnnotationsFile($annotations, $pluginName));
236242
}
237243

238244
/**
@@ -903,11 +909,11 @@ protected function getExampleIfAvailable(string $url, bool $useLocalToken = fals
903909
}
904910
$method = $queryParams['method'];
905911
$format = strtolower($queryParams['format']);
906-
$exampleFilePath = $this->currentPluginDir . OpenApiDocs::EXAMPLE_RESPONSES_PATH . $method . '.' . $format;
912+
[$pluginName, $methodName] = explode('.', $method);
913+
$exampleFilePath = $this->pathResolver->getExampleResponseFilePath($pluginName, $methodName, $format);
907914
// If there's already a file, use that instead of making a new server call. Ignore the file when the flag is set.
908915
if (!$ignoreCached) {
909916
// If an example file is found, return its contents instead of making the server call.
910-
[$pluginName, $methodName] = explode('.', $method);
911917
$exampleContents = $this->getCachedExampleResponseFile($pluginName, $methodName, $format);
912918
if (!empty($exampleContents)) {
913919
return $exampleContents;
@@ -962,7 +968,7 @@ protected function getExampleIfAvailable(string $url, bool $useLocalToken = fals
962968
$body = $response['data'];
963969

964970
// Write the example response to file as a cache and reference.
965-
file_put_contents($exampleFilePath, $body);
971+
$this->writeFile($exampleFilePath, $body);
966972

967973
// Convert the XML responses into a JSON object and then encode it into a string. This is helpful for building schemas.
968974
if ($format === 'xml') {
@@ -994,7 +1000,7 @@ protected function getExampleIfAvailable(string $url, bool $useLocalToken = fals
9941000
*/
9951001
protected function getCachedExampleResponseFile(string $pluginName, string $methodName, string $format, bool $rawResult = false, bool $applyMaxLength = true): string
9961002
{
997-
$exampleFilePath = $this->currentPluginDir . OpenApiDocs::EXAMPLE_RESPONSES_PATH . $pluginName . '.' . $methodName . '.' . $format;
1003+
$exampleFilePath = $this->pathResolver->getExampleResponseFilePath($pluginName, $methodName, $format);
9981004
// Simply return an empty string if the file doesn't exist yet.
9991005
if (!file_exists($exampleFilePath)) {
10001006
return '';
@@ -1021,6 +1027,14 @@ protected function getCachedExampleResponseFile(string $pluginName, string $meth
10211027
return $exampleContents;
10221028
}
10231029

1030+
protected function writeFile(string $filePath, string $contents)
1031+
{
1032+
$directory = dirname($filePath);
1033+
Filesystem::mkdir($directory);
1034+
1035+
return file_put_contents($filePath, $contents);
1036+
}
1037+
10241038
/**
10251039
* Try to build an example URL for a specific API method using report metadata. This queries the demo server for
10261040
* report metadata to get examples of existing reports which can be used as example URLS. If no metadata matches the

Annotations/ApiMethodInfoExtractor.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,24 @@
1414
use Piwik\Exception\PluginNotFoundException;
1515
use Piwik\API\Proxy;
1616
use Piwik\API\Request;
17+
use Piwik\Filesystem;
1718
use Piwik\Plugin\Manager;
18-
use Piwik\Plugins\OpenApiDocs\OpenApiDocs;
19+
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
1920
use Piwik\Validators\BaseValidator;
2021
use Piwik\Validators\NotEmpty;
2122

2223
class ApiMethodInfoExtractor
2324
{
25+
/**
26+
* @var PathResolver
27+
*/
28+
private $pathResolver;
29+
30+
public function __construct(?PathResolver $pathResolver = null)
31+
{
32+
$this->pathResolver = $pathResolver ?? new PathResolver();
33+
}
34+
2435
/**
2536
* Look up the Matomo Reporting API methods for the specified plugin(s) and output the basic information for each.
2637
* This includes the comment block, parameter information, and things like that. This can then be fed to a secure
@@ -38,8 +49,6 @@ public function extractMethodInfo(string $pluginName, bool $writeToFile = false)
3849
$pluginNames = explode(',', $pluginName);
3950

4051
BaseValidator::check('pluginNames', $pluginNames, [new NotEmpty()]);
41-
$currentPluginDir = Manager::getInstance()::getPluginDirectory('OpenApiDocs');
42-
4352
$methodInfoArray = [];
4453
foreach ($pluginNames as $plugin) {
4554
BaseValidator::check('pluginName', $plugin, [new NotEmpty()]);
@@ -63,7 +72,8 @@ public function extractMethodInfo(string $pluginName, bool $writeToFile = false)
6372
}
6473

6574
if ($writeToFile) {
66-
$pluginSpecPath = $currentPluginDir . OpenApiDocs::GENERATED_ANNOTATIONS_PATH . $fileBaseName . '_api_method_info.json';
75+
$pluginSpecPath = $this->pathResolver->getApiMethodInfoFilePath($fileBaseName);
76+
Filesystem::mkdir(dirname($pluginSpecPath));
6777
file_put_contents($pluginSpecPath, $methodInfoString);
6878
}
6979

Commands/ExtractReportingApiMethodInfo.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Piwik\Plugins\OpenApiDocs\Commands;
1313

14+
use Piwik\Container\StaticContainer;
1415
use Piwik\Plugin\ConsoleCommand;
1516
use Piwik\Plugins\OpenApiDocs\Annotations\ApiMethodInfoExtractor;
17+
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
1618

1719
/**
1820
* This class lets you define a new command. To read more about commands have a look at our Matomo Console guide on
@@ -86,7 +88,7 @@ protected function doExecute(): int
8688
$result = (new ApiMethodInfoExtractor())->extractMethodInfo($plugin, $notDryRun);
8789

8890
if ($notDryRun) {
89-
$output->writeln('<info>Results written to plugins/OpenApiDocs/tmp/annotations directory.</info>');
91+
$output->writeln('<info>Results written to ' . StaticContainer::get(PathResolver::class)->getAnnotationsDirectory() . '</info>');
9092

9193
return $result ? self::SUCCESS : self::FAILURE;
9294
}

Commands/GenerateAnnotations.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Piwik\Container\StaticContainer;
1313
use Piwik\Plugin\ConsoleCommand;
1414
use Piwik\Plugins\OpenApiDocs\Annotations\AnnotationGenerator;
15+
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
1516

1617
/**
1718
* This class lets you define a new command. To read more about commands have a look at our Matomo Console guide on
@@ -83,7 +84,7 @@ protected function doExecute(): int
8384
$result = (StaticContainer::get(AnnotationGenerator::class))->generatePluginApiAnnotations($plugin, $notDryRun);
8485

8586
if ($notDryRun) {
86-
$output->writeln('<info>Results written to plugins/OpenApiDocs/tmp/annotations/ directory.</info>');
87+
$output->writeln('<info>Results written to ' . StaticContainer::get(PathResolver::class)->getAnnotationsDirectory() . '</info>');
8788

8889
return $result ? self::SUCCESS : self::FAILURE;
8990
}

Commands/GenerateSpecFile.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Piwik\Plugin\ConsoleCommand;
1414
use Piwik\Plugins\OpenApiDocs\Generation\SpecGenerationService;
1515
use Piwik\Plugins\OpenApiDocs\OpenApiDocs;
16+
use Piwik\Plugins\OpenApiDocs\Specs\PathResolver;
1617

1718
/**
1819
* This class lets you define a new command. To read more about commands have a look at our Matomo Console guide on
@@ -28,9 +29,15 @@ class GenerateSpecFile extends ConsoleCommand
2829
*/
2930
private $specGenerationService;
3031

31-
public function __construct(?SpecGenerationService $specGenerationService = null)
32+
/**
33+
* @var PathResolver
34+
*/
35+
private $specPathResolver;
36+
37+
public function __construct(?SpecGenerationService $specGenerationService = null, ?PathResolver $specPathResolver = null)
3238
{
3339
$this->specGenerationService = $specGenerationService ?: StaticContainer::get(SpecGenerationService::class);
40+
$this->specPathResolver = $specPathResolver ?: StaticContainer::get(PathResolver::class);
3441

3542
parent::__construct();
3643
}
@@ -116,12 +123,12 @@ protected function doExecute(): int
116123

117124
if ($addAnnotations) {
118125
foreach ($pluginNames as $pluginName) {
119-
$output->writeln('<info>Created Annotations for ' . $pluginName . ' and wrote results to plugins/OpenApiDocs/tmp/annotations.</info>');
126+
$output->writeln('<info>Created Annotations for ' . $pluginName . ' and wrote results to ' . $this->specPathResolver->getAnnotationsDirectory() . '</info>');
120127
}
121128
}
122129

123130
if ($notDryRun) {
124-
$output->writeln('<info>Results written to plugins/OpenApiDocs/tmp/specs/ directory.</info>');
131+
$output->writeln('<info>Results written to ' . $this->specPathResolver->getSpecDirectory() . '</info>');
125132
return self::SUCCESS;
126133
}
127134

Specs/PathResolver.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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\Specs;
13+
14+
use Piwik\Container\Container;
15+
use Piwik\Container\StaticContainer;
16+
use Piwik\Plugin\Manager;
17+
use Piwik\Plugins\OpenApiDocs\OpenApiDocs;
18+
19+
class PathResolver
20+
{
21+
private const SHARED_BASE_SUBDIRECTORY = '/OpenApiDocs/';
22+
23+
private const SHARED_SPECS_SUBDIRECTORY = '/OpenApiDocs/specs/';
24+
25+
private const SHARED_ANNOTATIONS_SUBDIRECTORY = '/OpenApiDocs/annotations/';
26+
27+
private const SHARED_RESPONSES_SUBDIRECTORY = '/OpenApiDocs/responses/';
28+
29+
private $pluginDirectory;
30+
31+
private $isCloudActivated;
32+
33+
private $container;
34+
35+
public function __construct(?string $pluginDirectory = null, ?bool $isCloudActivated = null, ?Container $container = null)
36+
{
37+
$this->pluginDirectory = $pluginDirectory ?? Manager::getInstance()::getPluginDirectory('OpenApiDocs');
38+
$this->isCloudActivated = $isCloudActivated ?? Manager::getInstance()->isPluginActivated('Cloud');
39+
$this->container = $container ?? $this->getStaticContainer();
40+
}
41+
42+
public function getSpecDirectory(): string
43+
{
44+
return $this->getArtifactDirectory(self::SHARED_SPECS_SUBDIRECTORY, OpenApiDocs::GENERATED_SPECS_PATH);
45+
}
46+
47+
public function getSpecFilePath(
48+
string $specFileBaseName,
49+
string $version = OpenApiDocs::DEFAULT_SPEC_VERSION,
50+
string $format = 'json'
51+
): string {
52+
return $this->getSpecDirectory() . $specFileBaseName . '_openapi_spec_v' . $version . '.' . strtolower($format);
53+
}
54+
55+
public function getAnnotationsDirectory(): string
56+
{
57+
return $this->getArtifactDirectory(self::SHARED_ANNOTATIONS_SUBDIRECTORY, OpenApiDocs::GENERATED_ANNOTATIONS_PATH);
58+
}
59+
60+
public function getAnnotationFilePath(string $pluginName): string
61+
{
62+
return $this->getAnnotationsDirectory() . $pluginName . 'GeneratedAnnotations.php';
63+
}
64+
65+
public function getApiMethodInfoFilePath(string $fileBaseName): string
66+
{
67+
return $this->getAnnotationsDirectory() . $fileBaseName . '_api_method_info.json';
68+
}
69+
70+
public function getResponsesDirectory(): string
71+
{
72+
return $this->getArtifactDirectory(self::SHARED_RESPONSES_SUBDIRECTORY, OpenApiDocs::EXAMPLE_RESPONSES_PATH);
73+
}
74+
75+
public function getExampleResponseFilePath(string $pluginName, string $methodName, string $format): string
76+
{
77+
return $this->getResponsesDirectory() . $pluginName . '.' . $methodName . '.' . strtolower($format);
78+
}
79+
80+
private function getArtifactDirectory(string $sharedSubdirectory, string $fallbackPath): string
81+
{
82+
$sharedPath = $this->getSharedArtifactDirectory($sharedSubdirectory);
83+
if ($sharedPath !== null) {
84+
return $sharedPath;
85+
}
86+
87+
return $this->pluginDirectory . $fallbackPath;
88+
}
89+
90+
private function getSharedArtifactDirectory(string $sharedSubdirectory): ?string
91+
{
92+
if (!$this->isCloudActivated || $this->container === null || !$this->container->has('CloudDistributedCachePath')) {
93+
return null;
94+
}
95+
96+
$sharedBasePath = trim((string) $this->container->get('CloudDistributedCachePath'));
97+
if ($sharedBasePath === '') {
98+
return null;
99+
}
100+
101+
return rtrim($sharedBasePath, '/\\') . self::SHARED_BASE_SUBDIRECTORY . ltrim(substr($sharedSubdirectory, strlen(self::SHARED_BASE_SUBDIRECTORY)), '/\\');
102+
}
103+
104+
private function getStaticContainer(): ?Container
105+
{
106+
try {
107+
return StaticContainer::getContainer();
108+
} catch (\Throwable $e) {
109+
return null;
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)