Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"bin/sd-plugin-manager"
],
"require-dev": {
"mikey179/vfsstream": "^1.6",
"solutiondrive/standalone-build-tools": "^1.0"
},
"config": {
Expand Down
5 changes: 5 additions & 0 deletions spec/Provider/FilesystemProviderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public function it_can_load_file(): void
$this->loadFile(['src' => './test/file/path.zip'])->shouldReturn('./test/file/path.zip');
}

public function it_can_force_load_file(): void
{
$this->loadFile(['src' => './test/file/path.zip'], true)->shouldReturn('./test/file/path.zip');
}

public function it_cannot_load_with_empty_filename(): void
{
$this->shouldThrow(\RuntimeException::class)->during('loadFile', [[]]);
Expand Down
15 changes: 15 additions & 0 deletions spec/Provider/HttpProviderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ public function it_can_load_simple(Client $guzzleClient, ResponseInterface $guzz
$this->loadFile(['src' => $url]);
}

public function it_can_force_load_simple(Client $guzzleClient, ResponseInterface $guzzleResponse): void
{
$url = 'https://sd.test/url/to/file.zip';

$guzzleResponse->getStatusCode()->willReturn(200);
$guzzleClient->get(
Argument::exact($url),
Argument::withKey('sink')
)
->willReturn($guzzleResponse)
->shouldBeCalled();

$this->loadFile(['src' => $url], true);
}

public function it_can_load_with_auth(Client $guzzleClient, ResponseInterface $guzzleResponse): void
{
$url = 'https://sd.test/url/to/file.zip';
Expand Down
5 changes: 5 additions & 0 deletions spec/Provider/NoneProviderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public function it_can_load_file(): void
$this->loadFile([])->shouldReturn(null);
}

public function it_can_force_load_file(): void
{
$this->loadFile([], true)->shouldReturn(null);
}

public function it_supports(): void
{
$this->supports('none')->shouldReturn(true);
Expand Down
22 changes: 22 additions & 0 deletions spec/Provider/S3ProviderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ public function it_can_load_simple(
]);
}

public function it_can_force_load_simple(
S3ClientFactoryInterface $s3ClientFactory,
S3Client $client
): void {
$src = 'file.zip';

$s3ClientFactory->createClient(Argument::any(), Argument::any())
->shouldBeCalled()
->willReturn($client);

$client->getObject(
Argument::allOf(
Argument::withEntry('Bucket', self::BUCKET),
Argument::withEntry('Key', self::BASEPATH . '/' . $src)
)
);

$this->loadFile([
'src' => $src,
], true);
}

public function it_can_load_from_custom_region_and_profile(
S3ClientFactoryInterface $s3ClientFactory,
S3Client $client
Expand Down
18 changes: 17 additions & 1 deletion spec/Provider/StoreApiProviderSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function let(
public function it_can_load_a_plugin_with_correct_credentials(
StoreApiConnectorInterface $storeApiConnector
): void {
$storeApiConnector->loadPlugin('awesomePlugin', '0.0.2')
$storeApiConnector->loadPlugin('awesomePlugin', '0.0.2', false)
->willReturn('/tmp/plugin');

$this->loadFile(
Expand All @@ -55,6 +55,22 @@ public function it_can_load_a_plugin_with_correct_credentials(
->shouldReturn('/tmp/plugin');
}

public function it_can_force_load_a_plugin_with_correct_credentials(
StoreApiConnectorInterface $storeApiConnector
): void {
$storeApiConnector->loadPlugin('awesomePlugin', '0.0.2', true)
->willReturn('/tmp/plugin');

$this->loadFile(
[
'pluginId' => 'awesomePlugin',
'version' => '0.0.2',
],
true
)
->shouldReturn('/tmp/plugin');
}

public function it_supports(): void
{
$this->supports('http')->shouldReturn(false);
Expand Down
106 changes: 102 additions & 4 deletions spec/Service/StoreApiConnectorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RequestOptions;
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamDirectory;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Psr\Http\Message\StreamInterface;
use sd\SwPluginManager\Service\StoreApiConnector;
use sd\SwPluginManager\Service\StoreApiConnectorInterface;
Expand All @@ -26,6 +29,9 @@ class StoreApiConnectorSpec extends ObjectBehavior
const SHOPWARE_ACCOUNT_PASSWORD = 'SuperSecurePassword';
const SHOPWARE_SHOP_DOMAIN = 'example.org';

/** @var vfsStreamDirectory */
private $cacheRootDir;

public function it_is_initializable(): void
{
$this->shouldHaveType(StoreApiConnector::class);
Expand All @@ -40,9 +46,12 @@ public function let(
Client $guzzleClient,
StreamTranslatorInterface $streamTranslator
): void {
$this->cacheRootDir = vfsStream::setup('/tmp/');

$this->beConstructedWith(
$guzzleClient,
$streamTranslator
$streamTranslator,
$this->cacheRootDir->url()
);

// Resets environment variables on every run
Expand Down Expand Up @@ -116,7 +125,7 @@ public function it_can_load_a_plugin_only_if_domain_exists_in_partner_account(
RequestOptions::HEADERS => [
'X-Shopware-Token' => 'ABCDEF',
],
RequestOptions::SINK => '/tmp/sw-plugin-awesomePlugin0.0.2',
RequestOptions::SINK => $this->cacheRootDir->url() . '/sw-plugin-awesomePlugin0.0.2',
]
)
->shouldBeCalled()
Expand Down Expand Up @@ -189,7 +198,7 @@ public function it_can_load_a_plugin_only_if_domain_exists_in_normal_shop(
RequestOptions::HEADERS => [
'X-Shopware-Token' => 'ABCDEF',
],
RequestOptions::SINK => '/tmp/sw-plugin-awesomePlugin0.0.2',
RequestOptions::SINK => $this->cacheRootDir->url() . '/sw-plugin-awesomePlugin0.0.2',
]
)
->shouldBeCalled()
Expand Down Expand Up @@ -248,7 +257,7 @@ public function it_can_load_a_plugin_without_a_partner_account(
RequestOptions::HEADERS => [
'X-Shopware-Token' => 'ABCDEF',
],
RequestOptions::SINK => '/tmp/sw-plugin-awesomePlugin0.0.2',
RequestOptions::SINK => $this->cacheRootDir->url() . '/sw-plugin-awesomePlugin0.0.2',
]
)
->shouldBeCalled()
Expand All @@ -262,6 +271,95 @@ public function it_cannot_connect_to_store_api_without_credentials(): void
$this->shouldThrow(\RuntimeException::class)->during('loadPlugin', ['awesomePlugin', '0.0.2']);
}

public function it_does_not_download_plugin_if_it_is_available_in_cache(
Client $guzzleClient,
StreamTranslatorInterface $streamTranslator
): void {
vfsStream::newFile('sw-plugin-awesomePlugin0.0.2')->at($this->cacheRootDir);
vfsStream::newFile('sw-plugin-awesomePlugin1.2.5')->at($this->cacheRootDir);

$guzzleClient->get(Argument::any(), Argument::any())
->shouldNotBeCalled();
$guzzleClient->post(Argument::any(), Argument::any())
->shouldNotBeCalled();
$guzzleClient->put(Argument::any(), Argument::any())
->shouldNotBeCalled();
$guzzleClient->delete(Argument::any(), Argument::any())
->shouldNotBeCalled();
$guzzleClient->patch(Argument::any(), Argument::any())
->shouldNotBeCalled();

$streamTranslator->translateToArray(Argument::any())
->shouldNotBeCalled();

$this->loadPlugin('awesomePlugin', '0.0.2')
->shouldReturn($this->cacheRootDir->url() . '/sw-plugin-awesomePlugin0.0.2');
$this->loadPlugin('awesomePlugin', '1.2.5')
->shouldReturn($this->cacheRootDir->url() . '/sw-plugin-awesomePlugin1.2.5');
}

public function it_does_download_plugin_if_it_is_forced_even_if_plugin_is_available_in_cache(
Client $guzzleClient,
StreamTranslatorInterface $streamTranslator,
Response $accessTokenResponse,
StreamInterface $accessCodeStream,
Response $partnerResponse,
StreamInterface $partnerStream,
Response $shopsResponse,
StreamInterface $shopsStream,
Response $licenseResponse,
StreamInterface $licenseStream,
Response $pluginInfoResponse,
StreamInterface $pluginInfoStream,
Response $pluginResponse
): void {
\putenv('SHOPWARE_ACCOUNT_USER=' . self::SHOPWARE_ACCOUNT_USER);
\putenv('SHOPWARE_ACCOUNT_PASSWORD=' . self::SHOPWARE_ACCOUNT_PASSWORD);
\putenv('SHOPWARE_SHOP_DOMAIN=' . self::SHOPWARE_SHOP_DOMAIN);

vfsStream::newFile('sw-plugin-awesomePlugin0.0.2')->at($this->cacheRootDir);
vfsStream::newFile('sw-plugin-awesomePlugin1.2.5')->at($this->cacheRootDir);

// ACCESS TOKEN
$this->prepareAccessToken($guzzleClient, $streamTranslator, $accessTokenResponse, $accessCodeStream);

// CHECK FOR PARTNER ACCOUNT
$partnerData = [];
$this->preparePartnerAccountCheck($guzzleClient, $streamTranslator, $partnerResponse, $partnerStream, $partnerData);

// GET ALL SHOPS DIRECTLY CONNECTED TO ACCOUNT
$shopsData = [
[
'id' => 7,
'domain' => 'example.org',
],
];
$this->prepareShops($guzzleClient, $streamTranslator, $shopsResponse, $shopsStream, $shopsData);

// GET ALL LICENSES
$licenseUrl = '/shops/7/pluginlicenses';
$this->prepareLicenseData($guzzleClient, $streamTranslator, $licenseResponse, $licenseStream, $licenseUrl);

// GET ALL INFOS ABOUT PLUGIN
$pluginInfoUrl = '/shops/7/pluginlicenses/17';
$this->preparePluginInfoData($guzzleClient, $streamTranslator, $pluginInfoResponse, $pluginInfoStream, $pluginInfoUrl);

$downloadUrl = '/plugins/58/binaries/10/file?shopId=7';
$guzzleClient->get(
self::BASE_URL . $downloadUrl,
[
RequestOptions::HEADERS => [
'X-Shopware-Token' => 'ABCDEF',
],
RequestOptions::SINK => $this->cacheRootDir->url() . '/sw-plugin-awesomePlugin0.0.2',
]
)
->shouldBeCalled()
->willReturn($pluginResponse);

$this->loadPlugin('awesomePlugin', '0.0.2', true);
}

private function prepareAccessToken(
Client $guzzleClient,
StreamTranslatorInterface $streamTranslator,
Expand Down
29 changes: 28 additions & 1 deletion spec/Worker/PluginFetcherSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,39 @@ public function it_can_fetch(
->willReturn($provider);

$provider
->loadFile($providerParameters)
->loadFile($providerParameters, false)
->shouldBeCalled();

$this->fetch($configuredPluginState);
}

public function it_can_force_fetch(
ProviderRepositoryInterface $providerRepository,
ProviderInterface $provider,
ConfiguredPluginState $configuredPluginState
): void {
$providerParameters = [];

$configuredPluginState
->getProviderParameters()
->willReturn($providerParameters);

$configuredPluginState
->getProvider()
->willReturn('testType');

$providerRepository
->getProviderSupporting(Argument::exact('testType'))
->shouldBeCalled()
->willReturn($provider);

$provider
->loadFile($providerParameters, true)
->shouldBeCalled();

$this->fetch($configuredPluginState, true);
}

public function it_can_throw_no_suitable_provider_exception(
ProviderRepositoryInterface $providerRepository,
ConfiguredPluginState $configuredPluginState
Expand Down
9 changes: 8 additions & 1 deletion src/Command/AutomaticDeployCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ protected function configure(): void
InputOption::VALUE_NONE,
'If set, skip download and extraction'
)
->addOption(
'force-download',
'',
InputOption::VALUE_NONE,
'If set, a download will be forced (not using any cache)! Be careful: If skip-download is set, this parameter does not do anything!'
)
->addOption(
'skip-install',
'',
Expand All @@ -89,6 +95,7 @@ protected function execute(
OutputInterface $output
) {
$skipDownload = (bool) $input->getOption('skip-download');
$forceDownload = (bool) $input->getOption('force-download');
$skipInstall = (bool) $input->getOption('skip-install');

$environment = $input->getOption('env');
Expand All @@ -114,7 +121,7 @@ protected function execute(

// Download the plugin and get the path
$output->write('Downloading plugin `' . $configuredPluginState->getId() . '`...');
$downloadPath = $this->pluginFetcher->fetch($configuredPluginState);
$downloadPath = $this->pluginFetcher->fetch($configuredPluginState, $forceDownload);
$output->writeln(' <info>done.</info>');

// If no path was returned, it is assumed that the plugin is already in its destination path
Expand Down
10 changes: 9 additions & 1 deletion src/Command/FetchPluginCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ protected function configure(): void
'The current environment to use for calling shopware commands',
'production'
)
->addOption(
'force',
'',
InputOption::VALUE_NONE,
'If set, no cache will be used to get the plugin'
)
->setDescription('Fetches a plugin from its source.')
->setHelp(
'Fetches a plugin from its source configured in the given statefile.'
Expand All @@ -85,7 +91,9 @@ protected function execute(
throw new \RuntimeException('The given plugin was not found in the statefile.');
}

$file = $this->pluginFetcher->fetch($configuredPluginState);
$force = (bool) $input->getOption('force');

$file = $this->pluginFetcher->fetch($configuredPluginState, $force);

$output->writeln("Downloaded plugin '$pluginId' to: ", OutputInterface::VERBOSITY_VERBOSE);
$output->writeln($file);
Expand Down
2 changes: 1 addition & 1 deletion src/Provider/FilesystemProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class FilesystemProvider implements ProviderInterface
/**
* {@inheritdoc}
*/
public function loadFile(array $parameters): ?string
public function loadFile(array $parameters, bool $force = false): ?string
{
if (true === empty($parameters['src'])) {
throw new \RuntimeException('src must not be empty for FilesystemProvider.');
Expand Down
2 changes: 1 addition & 1 deletion src/Provider/HttpProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function __construct(Client $guzzleClient)
/**
* {@inheritdoc}
*/
public function loadFile(array $parameters): ?string
public function loadFile(array $parameters, bool $force = false): ?string
{
if (true === empty($parameters['src'])) {
throw new \RuntimeException('src must not be empty for HttpProvider.');
Expand Down
2 changes: 1 addition & 1 deletion src/Provider/NoneProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class NoneProvider implements ProviderInterface
/**
* {@inheritdoc}
*/
public function loadFile(array $parameters): ?string
public function loadFile(array $parameters, bool $force = false): ?string
{
return null;
}
Expand Down
Loading