Upstream pelican/panel state of the touched files before 4334d5141e
This commit is contained in:
1 parent
7e79170bf9
commit
f81f44a004
3 files changed
+192
-59
No files matched your search
@@ -0,0 +1,191 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\Application\Plugins;
|
||||||
|
|
||||||
|
use App\Data\Api\Application\PluginData;
|
||||||
|
use App\Enums\PluginStatus;
|
||||||
|
use App\Exceptions\PanelException;
|
||||||
|
use App\Http\Controllers\Api\Application\ApplicationApiController;
|
||||||
|
use App\Http\Requests\Api\Application\Plugins\ImportFilePluginRequest;
|
||||||
|
use App\Http\Requests\Api\Application\Plugins\ReadPluginRequest;
|
||||||
|
use App\Http\Requests\Api\Application\Plugins\UninstallPluginRequest;
|
||||||
|
use App\Http\Requests\Api\Application\Plugins\WritePluginRequest;
|
||||||
|
use App\Models\Plugin;
|
||||||
|
use App\Services\Helpers\PluginService;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use Spatie\QueryBuilder\QueryBuilder;
|
||||||
|
|
||||||
|
class PluginController extends ApplicationApiController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* PluginController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct(private readonly PluginService $pluginService)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List plugins
|
||||||
|
*
|
||||||
|
* Return all plugins on the Panel.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*/
|
||||||
|
public function index(ReadPluginRequest $request): array
|
||||||
|
{
|
||||||
|
$plugins = QueryBuilder::for(Plugin::class)
|
||||||
|
->allowedFilters('id', 'name', 'author', 'category')
|
||||||
|
->allowedSorts('id', 'name', 'author', 'category')
|
||||||
|
->paginate($request->query('per_page') ?? 10);
|
||||||
|
|
||||||
|
return $this->response->collection($plugins)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View plugin
|
||||||
|
*
|
||||||
|
* Return a single plugin.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*/
|
||||||
|
public function view(ReadPluginRequest $request, Plugin $plugin): array
|
||||||
|
{
|
||||||
|
return $this->response->item($plugin)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import plugin (file)
|
||||||
|
*
|
||||||
|
* Imports a new plugin file.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function importFile(WritePluginRequest $request): Response
|
||||||
|
{
|
||||||
|
throw_unless($request->hasFile('plugin'), new PanelException("No 'plugin' file in request"));
|
||||||
|
|
||||||
|
$this->pluginService->downloadPluginFromFile($request->file('plugin'));
|
||||||
|
|
||||||
|
return new Response('', Response::HTTP_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import plugin (url)
|
||||||
|
*
|
||||||
|
* Imports a new plugin from an url.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function importUrl(ImportFilePluginRequest $request): Response
|
||||||
|
{
|
||||||
|
$this->pluginService->downloadPluginFromUrl($request->input('url'));
|
||||||
|
|
||||||
|
return new Response('', Response::HTTP_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install plugin
|
||||||
|
*
|
||||||
|
* Installs and enables a plugin.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function install(WritePluginRequest $request, Plugin $plugin): array
|
||||||
|
{
|
||||||
|
throw_if($plugin->status !== PluginStatus::NotInstalled, new PanelException('Plugin is already installed'));
|
||||||
|
|
||||||
|
$this->pluginService->installPlugin($plugin);
|
||||||
|
|
||||||
|
return $this->response->item($plugin)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update plugin
|
||||||
|
*
|
||||||
|
* Downloads and installs an update for a plugin. Will throw if no update is available.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function update(WritePluginRequest $request, Plugin $plugin): array
|
||||||
|
{
|
||||||
|
throw_unless($plugin->isUpdateAvailable(), new PanelException("Plugin doesn't need updating"));
|
||||||
|
|
||||||
|
$this->pluginService->updatePlugin($plugin);
|
||||||
|
|
||||||
|
return $this->response->item($plugin)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninstall plugin
|
||||||
|
*
|
||||||
|
* Uninstalls a plugin. Optionally it will delete the plugin folder too.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function uninstall(UninstallPluginRequest $request, Plugin $plugin): array
|
||||||
|
{
|
||||||
|
throw_if($plugin->status === PluginStatus::NotInstalled, new PanelException('Plugin is not installed'));
|
||||||
|
|
||||||
|
$this->pluginService->uninstallPlugin($plugin, $request->boolean('delete'));
|
||||||
|
|
||||||
|
return $this->response->item($plugin)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable plugin
|
||||||
|
*
|
||||||
|
* Enables a plugin.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function enable(WritePluginRequest $request, Plugin $plugin): array
|
||||||
|
{
|
||||||
|
throw_unless($plugin->canEnable(), new PanelException("Plugin can't be enabled"));
|
||||||
|
|
||||||
|
$this->pluginService->enablePlugin($plugin);
|
||||||
|
|
||||||
|
return $this->response->item($plugin)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable plugin
|
||||||
|
*
|
||||||
|
* Disables a plugin.
|
||||||
|
*
|
||||||
|
* @return array<array-key, mixed>
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function disable(WritePluginRequest $request, Plugin $plugin): array
|
||||||
|
{
|
||||||
|
throw_unless($plugin->canDisable(), new PanelException("Plugin can't be disabled"));
|
||||||
|
|
||||||
|
$this->pluginService->disablePlugin($plugin);
|
||||||
|
|
||||||
|
return $this->response->item($plugin)
|
||||||
|
->transformWith(PluginData::class)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -321,7 +321,7 @@ class PluginService
|
|||||||
$this->downloadPluginFromUrl($downloadUrl, $plugin->id);
|
$this->downloadPluginFromUrl($downloadUrl, $plugin->id);
|
||||||
|
|
||||||
Plugin::refreshRows();
|
Plugin::refreshRows();
|
||||||
$plugin = Plugin::findOrFail($plugin->id);
|
$plugin = $plugin->refresh();
|
||||||
|
|
||||||
$this->installPlugin($plugin, false);
|
$this->installPlugin($plugin, false);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Tests\Integration\Services;
|
namespace App\Tests\Integration\Services;
|
||||||
|
|
||||||
use App\Enums\PluginStatus;
|
|
||||||
use App\Models\Plugin;
|
|
||||||
use App\Services\Helpers\PluginService;
|
use App\Services\Helpers\PluginService;
|
||||||
use App\Tests\Integration\IntegrationTestCase;
|
use App\Tests\Integration\IntegrationTestCase;
|
||||||
use Exception;
|
use Exception;
|
||||||
@@ -11,7 +9,6 @@ use Illuminate\Filesystem\Filesystem;
|
|||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
@@ -46,9 +43,6 @@ class PluginServiceTest extends IntegrationTestCase
|
|||||||
File::deleteDirectory($leftover);
|
File::deleteDirectory($leftover);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't leak the in-memory Sushi rows of the plugins removed above into later tests.
|
|
||||||
Plugin::refreshRows();
|
|
||||||
|
|
||||||
// TemporaryDirectory::make() does not clean up on its own; remove the upload fixtures.
|
// TemporaryDirectory::make() does not clean up on its own; remove the upload fixtures.
|
||||||
foreach ($this->uploadDirs as $dir) {
|
foreach ($this->uploadDirs as $dir) {
|
||||||
$dir->delete();
|
$dir->delete();
|
||||||
@@ -394,44 +388,6 @@ class PluginServiceTest extends IntegrationTestCase
|
|||||||
$this->assertDirectoryDoesNotExist(plugin_path('.test-clean-plugin.bak'));
|
$this->assertDirectoryDoesNotExist(plugin_path('.test-clean-plugin.bak'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_update_downloads_and_reinstalls_the_new_version(): void
|
|
||||||
{
|
|
||||||
$this->importedPlugins[] = 'test-update-plugin';
|
|
||||||
|
|
||||||
// Updates are resolved against the panel version, which is null on canary.
|
|
||||||
config()->set('app.version', '1.0.0');
|
|
||||||
Process::fake();
|
|
||||||
|
|
||||||
$updateUrl = 'https://example.test/test-update-plugin/update.json';
|
|
||||||
$downloadUrl = 'https://example.test/test-update-plugin/2.0.0.zip';
|
|
||||||
|
|
||||||
$this->service->downloadPluginFromFile($this->makeUpload('test-update-plugin.zip', [
|
|
||||||
'test-update-plugin/plugin.json' => $this->updatableManifest('test-update-plugin', '1.0.0', $updateUrl),
|
|
||||||
]));
|
|
||||||
|
|
||||||
$update = file_get_contents($this->makeUpload('2.0.0.zip', [
|
|
||||||
'test-update-plugin/plugin.json' => $this->updatableManifest('test-update-plugin', '2.0.0', $updateUrl),
|
|
||||||
])->getPathname());
|
|
||||||
|
|
||||||
Http::fake([
|
|
||||||
$updateUrl => Http::response(['*' => ['version' => '2.0.0', 'download_url' => $downloadUrl]]),
|
|
||||||
$downloadUrl => Http::response($update),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Plugin::refreshRows();
|
|
||||||
$plugin = Plugin::findOrFail('test-update-plugin');
|
|
||||||
|
|
||||||
$this->service->updatePlugin($plugin);
|
|
||||||
|
|
||||||
$this->assertSame('2.0.0', $this->installedVersion('test-update-plugin'));
|
|
||||||
|
|
||||||
// installPlugin() ran against the reloaded row and recorded the plugin as installed but disabled.
|
|
||||||
$this->assertSame(PluginStatus::Disabled->value, File::json(plugin_path('test-update-plugin', 'plugin.json'))['meta']['status']);
|
|
||||||
|
|
||||||
Plugin::refreshRows();
|
|
||||||
$this->assertSame('2.0.0', Plugin::findOrFail('test-update-plugin')->version);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function installedVersion(string $id): string
|
private function installedVersion(string $id): string
|
||||||
{
|
{
|
||||||
return File::json(plugin_path($id, 'plugin.json'))['version'];
|
return File::json(plugin_path($id, 'plugin.json'))['version'];
|
||||||
@@ -442,20 +398,6 @@ class PluginServiceTest extends IntegrationTestCase
|
|||||||
return json_encode(['id' => $id, 'version' => $version]);
|
return json_encode(['id' => $id, 'version' => $version]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updatableManifest(string $id, string $version, string $updateUrl): string
|
|
||||||
{
|
|
||||||
return json_encode([
|
|
||||||
'id' => $id,
|
|
||||||
'name' => 'Test Update Plugin',
|
|
||||||
'author' => 'Pelican',
|
|
||||||
'version' => $version,
|
|
||||||
'category' => 'plugin',
|
|
||||||
'update_url' => $updateUrl,
|
|
||||||
'namespace' => 'TestUpdatePlugin',
|
|
||||||
'class' => 'TestUpdatePlugin',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a zip on disk from the given [path => contents] map and wrap it in an UploadedFile.
|
* Build a zip on disk from the given [path => contents] map and wrap it in an UploadedFile.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in new issue
Block a user