Skip to content

Commit

Permalink
Plus Module Sources management to current process and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Quetzacoalt91 committed Aug 30, 2024
1 parent 84eea40 commit 4b35332
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 90 deletions.
2 changes: 1 addition & 1 deletion classes/Task/Upgrade/UpgradeModules.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function run(): int

$modulesPath = $this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR;

$moduleDownloader = new ModuleDownloader($this->translator, $this->logger, $this->container->getState()->getInstallVersion());
$moduleDownloader = new ModuleDownloader($this->translator, $this->logger);
$moduleUnzipper = new ModuleUnzipper($this->translator, $this->container->getZipAction(), $modulesPath);
$moduleMigration = new ModuleMigration($this->translator, $this->logger);

Expand Down
104 changes: 33 additions & 71 deletions classes/UpgradeTools/Module/ModuleDownloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@

namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;

use Exception;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\Log\Logger;
use PrestaShop\Module\AutoUpgrade\Tools14;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
Expand All @@ -41,100 +41,62 @@ class ModuleDownloader
/** @var Logger */
private $logger;

/** @var string */
private $psVersion;

/** @var string */
private $addonsUrl = 'api.addons.prestashop.com';

public function __construct(Translator $translator, Logger $logger, string $psVersion)
public function __construct(Translator $translator, Logger $logger)
{
$this->translator = $translator;
$this->logger = $logger;
$this->psVersion = $psVersion;
}

/**
* @throws UpgradeException
*/
public function downloadModule(ModuleDownloaderContext $moduleDownloaderContext): void
{
$localModuleUsed = false;

if ($moduleDownloaderContext->getModuleIsLocal()) {
$localModuleUsed = $this->downloadModuleFromLocalZip($moduleDownloaderContext);
}

if (!$localModuleUsed) {
$this->downloadModuleFromAddons($moduleDownloaderContext);
}

if (filesize($moduleDownloaderContext->getZipFullPath()) <= 300) {
throw (new UpgradeException($this->translator->trans('[WARNING] An error occurred while downloading module %s, the received file is empty.', [$moduleDownloaderContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
}

private function downloadModuleFromLocalZip(ModuleDownloaderContext $moduleDownloaderContext): bool
{
try {
$localModuleZip = $this->getLocalModuleZipPath($moduleDownloaderContext->getModuleName());
if (empty($localModuleZip)) {
return false;
for ($i = 0; $i < count($moduleDownloaderContext->getUpdateSources()); ++$i) {
try {
$this->attemptDownload($moduleDownloaderContext, $i);
} catch (Exception $e) {
$this->logger->debug($e->getMessage());
$this->logger->debug($this->translator->trans('Download of source #%s has failed.', [$i]));

if ($i + 1 === count($moduleDownloaderContext->getUpdateSources())) {
throw (new UpgradeException('All download attempts have failed. Check your environment and try again.'))->setSeverity(UpgradeException::SEVERITY_ERROR);
}
}
$filesystem = new Filesystem();
$filesystem->copy($localModuleZip, $moduleDownloaderContext->getZipFullPath());
unlink($localModuleZip);
$this->logger->notice($this->translator->trans('Local module %s successfully copied.', [$moduleDownloaderContext->getModuleName()]));

return true;
} catch (IOException $e) {
$this->logger->notice($this->translator->trans('Can not found or copy local module %s. Trying to download it from Addons.', [$moduleDownloaderContext->getModuleName()]));
}

return false;
$this->logger->notice($this->translator->trans('Module %s has been successfully downloaded.', [$moduleDownloaderContext->getModuleName()]));
}

/**
* @throws UpgradeException
* @throws IOException When copy fails
* @throws UpgradeException If download content is invalid
*/
private function downloadModuleFromAddons(ModuleDownloaderContext $moduleDownloaderContext): void
private function attemptDownload(ModuleDownloaderContext $moduleDownloaderContext, int $index): void
{
$addonsUrl = extension_loaded('openssl')
? 'https://' . $this->addonsUrl
: 'http://' . $this->addonsUrl;

// Make the request
$context = stream_context_create([
'http' => [
'method' => 'POST',
'content' => 'version=' . $this->psVersion . '&method=module&id_module=' . $moduleDownloaderContext->getModuleId(),
'header' => 'Content-type: application/x-www-form-urlencoded',
'timeout' => 10,
],
]);

// file_get_contents can return false if https is not supported (or warning)
$content = Tools14::file_get_contents($addonsUrl, false, $context);
if (empty($content) || substr($content, 5) == '<?xml') {
throw (new UpgradeException($this->translator->trans('[WARNING] No response from Addons server.')))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
$moduleSource = $moduleDownloaderContext->getUpdateSources()[$index];
$filesystem = new Filesystem();

if (false === (bool) file_put_contents($moduleDownloaderContext->getZipFullPath(), $content)) {
throw (new UpgradeException($this->translator->trans('[WARNING] Unable to write module %s\'s zip file in temporary directory.', [$moduleDownloaderContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
if ($moduleSource->isZipped()) {
$filesystem->copy($moduleSource->getPath(), $moduleDownloaderContext->getDestinationFolder());
$this->assertDownloadedFileIsCorrect($moduleDownloaderContext);
return;
}

$this->logger->notice($this->translator->trans('Module %s has been successfully downloaded from Addons.', [$moduleDownloaderContext->getModuleName()]));

// Module contents is already unzipped
$filesystem->mirror($moduleSource->getPath(), $moduleDownloaderContext->getDestinationFolder());
}

private function getLocalModuleZipPath(string $name): ?string
private function assertDownloadedFileIsCorrect(ModuleDownloaderContext $moduleDownloaderContext)

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.2.5)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.3.4)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.4.4)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.5.1)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.6)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.7)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.8)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.0.0)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.

Check failure on line 90 in classes/UpgradeTools/Module/ModuleDownloader.php

View workflow job for this annotation

GitHub Actions / PHPStan (latest)

Method PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\ModuleDownloader::assertDownloadedFileIsCorrect() has no return type specified.
{
$autoUpgradeDir = _PS_ADMIN_DIR_ . DIRECTORY_SEPARATOR . 'autoupgrade';
$module_zip = $autoUpgradeDir . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $name . '.zip';

if (file_exists($module_zip) && is_readable($module_zip)) {
return $module_zip;
if (filesize($moduleDownloaderContext->getDestinationFolder()) <= 300) {
throw (new UpgradeException($this->translator->trans('[WARNING] An error occurred while downloading module %s, the received file is empty.', [$moduleDownloaderContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}

return null;
$downloadedFile = fopen($moduleDownloaderContext->getDestinationFolder(), 'r');
if (!$downloadedFile || fread($downloadedFile, 5) == '<?xml') {
throw (new UpgradeException($this->translator->trans('[WARNING] No response from provider.')))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
fclose($downloadedFile);
}
}
23 changes: 20 additions & 3 deletions classes/UpgradeTools/Module/ModuleDownloaderContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;

use LogicException;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Module\Source\ModuleSource;

class ModuleDownloaderContext
{
/** @var string */
private $destinationFolder;

/** @var string */
private $moduleName;

Expand All @@ -39,22 +43,35 @@ class ModuleDownloaderContext
/** @var ModuleSource[]|null */
private $updateSources;

public function __construct(string $moduleName, string $referenceVersion)
/**
* @param array{name:string, currentVersion:string} $moduleInfos
*/
public function __construct(string $destinationFolder, array $moduleInfos)
{
$this->moduleName = $moduleName;
$this->referenceVersion = $referenceVersion;
$this->destinationFolder = $destinationFolder;
$this->moduleName = $moduleInfos['name'];
$this->referenceVersion = $moduleInfos['currentVersion'];
}

/**
* @throws LogicException
*/
public function validate(): void
{
if (empty($this->destinationFolder)) {
throw new LogicException('Destination path is invalid.');
}

if (empty($this->updateSources)) {
throw new LogicException('List of updates is invalid.');
}
}

public function getDestinationFolder(): string
{
return $this->destinationFolder;
}

public function getModuleName(): string
{
return $this->moduleName;
Expand Down
2 changes: 1 addition & 1 deletion classes/UpgradeTools/Module/ModuleUnzipper.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function __construct(Translator $translator, ZipAction $zipAction, string
*/
public function unzipModule(ModuleUnzipperContext $moduleUnzipperContext): void
{
if (!$this->zipAction->extract($moduleUnzipperContext->getZipFullPath(), $this->modulesPath)) {
if (!$this->zipAction->extract($moduleUnzipperContext->getDestinationFilePath(), $this->modulesPath)) {
throw (new UpgradeException($this->translator->trans('[WARNING] Error when trying to extract module %s.', [$moduleUnzipperContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
}
Expand Down
2 changes: 1 addition & 1 deletion classes/UpgradeTools/Module/ModuleUnzipperContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private function validate(): void
}
}

public function getZipFullPath(): string
public function getDestinationFilePath(): string
{
return $this->zipFullPath;
}
Expand Down
11 changes: 6 additions & 5 deletions classes/UpgradeTools/Module/Source/ModuleSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

class ModuleSource
{
/** var string */
/** @var string */
private $name;

/** var string */
/** @var string */
private $newVersion;

/** var string */
/** @var string */
private $path;

/** var bool */
/** @var bool */
private $unzipable;

public function __construct(string $name, string $newVersion, string $path, bool $unzipable)
Expand All @@ -39,11 +39,12 @@ public function getPath(): string
return $this->path;
}

public function isUnzipable(): bool
public function isZipped(): bool
{
return $this->unzipable;
}

/** @return array<string, string|boolean> */
public function toArray(): array
{
return [
Expand Down
5 changes: 1 addition & 4 deletions classes/UpgradeTools/Module/Source/ModuleSourceAggregate.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ public function __construct(array $sourceProviders)
$this->providers = $sourceProviders;
}

/**
* @return ModuleSource[]
*/
public function setSourcesIn(ModuleDownloaderContext $moduleContext): void
{
$updateSources = [];
Expand All @@ -39,7 +36,7 @@ public function setSourcesIn(ModuleDownloaderContext $moduleContext): void
}

/**
* @param ModuleSource[]
* @param ModuleSource[] $sources
*
* @return ModuleSource[]
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function testConstructWithCorrectSettings()

$moduleDownloaderContext = new ModuleDownloaderContext($zipFullPath, $moduleInfos);

$this->assertEquals($zipFullPath, $moduleDownloaderContext->getZipFullPath());
$this->assertEquals($zipFullPath, $moduleDownloaderContext->getDestinationFilePath());
$this->assertEquals('mymodule', $moduleDownloaderContext->getModuleName());
$this->assertEquals(1245, $moduleDownloaderContext->getModuleId());
$this->assertTrue($moduleDownloaderContext->getModuleIsLocal());
Expand All @@ -56,7 +56,7 @@ public function testConstructWithCorrectSettingsAndNotIsLocal()

$moduleDownloaderContext = new ModuleDownloaderContext($zipFullPath, $moduleInfos);

$this->assertEquals($zipFullPath, $moduleDownloaderContext->getZipFullPath());
$this->assertEquals($zipFullPath, $moduleDownloaderContext->getDestinationFilePath());
$this->assertEquals('mymodule', $moduleDownloaderContext->getModuleName());
$this->assertEquals(1245, $moduleDownloaderContext->getModuleId());
$this->assertFalse($moduleDownloaderContext->getModuleIsLocal());
Expand Down
Loading

0 comments on commit 4b35332

Please sign in to comment.