Skip to content

Commit

Permalink
Merge pull request #23 from shopware/feat-add-fastly-support
Browse files Browse the repository at this point in the history
feat: add fastly support, fixes #19
  • Loading branch information
shyim authored Oct 21, 2024
2 parents bc86fb6 + 99d81ad commit fa8b9a0
Show file tree
Hide file tree
Showing 15 changed files with 1,022 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\XmlDumper;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\Filesystem\Filesystem;

Expand Down Expand Up @@ -42,6 +43,10 @@ private function createContainer(): ContainerBuilder
$definition->addTag('console.command');
});

$container->registerAttributeForAutoconfiguration(AsEventListener::class, function (ChildDefinition $definition): void {
$definition->addTag('kernel.event_listener');
});

$container->addCompilerPass(new AddConsoleCommandPass());
$container->addCompilerPass(new RegisterListenersPass());

Expand Down
54 changes: 54 additions & 0 deletions src/Command/FastlySnippetListCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Command;

use Shopware\Deployment\Helper\EnvironmentHelper;
use Shopware\Deployment\Integration\Fastly\FastlyAPIClient;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'fastly:snippet:list',
description: 'List all Fastly snippets'
)]
class FastlySnippetListCommand extends Command
{
public function __construct(private readonly FastlyAPIClient $fastlyAPIClient)
{
parent::__construct();
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$apiToken = EnvironmentHelper::getVariable('FASTLY_API_TOKEN', '');
$serviceId = EnvironmentHelper::getVariable('FASTLY_SERVICE_ID', '');

if ($apiToken === '' || $serviceId === '') {
$output->writeln('FASTLY_API_TOKEN or FASTLY_SERVICE_ID is not set.');

return self::FAILURE;
}

$this->fastlyAPIClient->setApiKey($apiToken);

$currentlyActiveVersion = $this->fastlyAPIClient->getCurrentlyActiveVersion($serviceId);

$snippets = $this->fastlyAPIClient->listSnippets($serviceId, $currentlyActiveVersion);

$table = new Table($output);
$table->setHeaders(['Name', 'Type', 'Priority', 'Last change']);

foreach ($snippets as $snippet) {
$table->addRow([$snippet['name'], $snippet['type'], $snippet['priority'], $snippet['updated_at']]);
}

$table->render();

return self::SUCCESS;
}
}
58 changes: 58 additions & 0 deletions src/Command/FastlySnippetRemoveCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Command;

use Shopware\Deployment\Helper\EnvironmentHelper;
use Shopware\Deployment\Integration\Fastly\FastlyAPIClient;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'fastly:snippet:remove',
description: 'Remove a Fastly snippet'
)]
class FastlySnippetRemoveCommand extends Command
{
public function __construct(private readonly FastlyAPIClient $fastlyAPIClient)
{
parent::__construct();
}

protected function configure(): void
{
$this->addArgument('snippetName', InputArgument::REQUIRED, 'The name of the snippet to remove');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$apiToken = EnvironmentHelper::getVariable('FASTLY_API_TOKEN', '');
$serviceId = EnvironmentHelper::getVariable('FASTLY_SERVICE_ID', '');

if ($apiToken === '' || $serviceId === '') {
$output->writeln('FASTLY_API_TOKEN or FASTLY_SERVICE_ID is not set.');

return self::FAILURE;
}

$this->fastlyAPIClient->setApiKey($apiToken);

$currentlyActiveVersion = $this->fastlyAPIClient->getCurrentlyActiveVersion($serviceId);

$snippetName = $input->getArgument('snippetName');

$newVersionId = $this->fastlyAPIClient->cloneServiceVersion($serviceId, $currentlyActiveVersion);

$this->fastlyAPIClient->deleteSnippet($serviceId, $newVersionId, $snippetName);

$this->fastlyAPIClient->activateServiceVersion($serviceId, $newVersionId);

$output->writeln('Snippet removed.');

return self::SUCCESS;
}
}
5 changes: 5 additions & 0 deletions src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Shopware\Deployment\Command;

use Shopware\Deployment\Event\PostDeploy;
use Shopware\Deployment\Services\HookExecutor;
use Shopware\Deployment\Services\InstallationManager;
use Shopware\Deployment\Services\ShopwareState;
Expand All @@ -14,6 +15,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

#[AsCommand('run', description: 'Install or Update Shopware')]
class RunCommand extends Command
Expand All @@ -23,6 +25,7 @@ public function __construct(
private readonly InstallationManager $installationManager,
private readonly UpgradeManager $upgradeManager,
private readonly HookExecutor $hookExecutor,
private readonly EventDispatcherInterface $eventDispatcher,
) {
parent::__construct();
}
Expand Down Expand Up @@ -55,6 +58,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->installationManager->run($config, $output);
}

$this->eventDispatcher->dispatch(new PostDeploy($config, $output));

$this->hookExecutor->execute(HookExecutor::HOOK_POST);

return Command::SUCCESS;
Expand Down
17 changes: 17 additions & 0 deletions src/Event/PostDeploy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Event;

use Shopware\Deployment\Struct\RunConfiguration;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Attribute\Exclude;

#[Exclude]
readonly class PostDeploy
{
public function __construct(public readonly RunConfiguration $configuration, public readonly OutputInterface $output)
{
}
}
94 changes: 94 additions & 0 deletions src/Integration/Fastly/FastlyAPIClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Integration\Fastly;

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
* @phpstan-type Snippet array{name: string, type: string, content: string, priority: int, updated_at: string}
*/
class FastlyAPIClient
{
private HttpClientInterface $client;
private HttpClientInterface $baseClient;

public function __construct(?HttpClientInterface $baseClient = null)
{
$this->baseClient = $baseClient ?? HttpClient::createForBaseUri('https://api.fastly.com');
}

public function setApiKey(string $apiKey): void
{
$this->client = $this->baseClient->withOptions([
'headers' => [
'Fastly-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
}

/**
* @return Snippet[]
*/
public function listSnippets(string $serviceId, int $version): array
{
$snippets = $this->client->request('GET', \sprintf('/service/%s/version/%d/snippet', $serviceId, $version))->toArray();

return $snippets;
}

public function createSnippet(string $serviceId, int $version, string $name, string $type, string $content, int $priority): void
{
$this->client->request('POST', \sprintf('/service/%s/version/%d/snippet', $serviceId, $version), [
'json' => [
'name' => $name,
'type' => $type,
'content' => $content,
'dynamic' => 0,
'priority' => $priority,
],
]);
}

public function updateSnippet(string $serviceId, int $version, string $name, string $type, string $content, int $priority): void
{
$this->client->request('PUT', \sprintf('/service/%s/version/%d/snippet/%s', $serviceId, $version, $name), [
'json' => [
'type' => $type,
'content' => $content,
'priority' => $priority,
],
]);
}

public function deleteSnippet(string $serviceId, int $version, string $name): void
{
$this->client->request('DELETE', \sprintf('/service/%s/version/%d/snippet/%s', $serviceId, $version, $name))->toArray();
}

public function cloneServiceVersion(string $serviceId, int $version): int
{
$response = $this->client->request('PUT', \sprintf('/service/%s/version/%d/clone', $serviceId, $version));

$data = $response->toArray();

return $data['number'];
}

public function activateServiceVersion(string $serviceId, int $version): void
{
$this->client->request('PUT', \sprintf('/service/%s/version/%d/activate', $serviceId, $version))->toArray();
}

public function getCurrentlyActiveVersion(string $serviceId): int
{
$response = $this->client->request('GET', \sprintf('/service/%s/version/active', $serviceId));

$data = $response->toArray();

return $data['number'];
}
}
47 changes: 47 additions & 0 deletions src/Integration/Fastly/FastlyContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Integration\Fastly;

use Symfony\Component\DependencyInjection\Attribute\Exclude;

/**
* @phpstan-import-type Snippet from FastlyAPIClient
*/
#[Exclude]
class FastlyContext
{
/**
* @param Snippet[] $snippets
*/
public function __construct(
public array $snippets,
public string $serviceId,
public int $currentlyActiveVersion,
public ?int $createdVersion = null)
{
}

public function hasSnippet(string $snippetName): bool
{
foreach ($this->snippets as $snippet) {
if ($snippet['name'] === $snippetName) {
return true;
}
}

return false;
}

public function hasSnippetChanged(string $snippetName, string $content): bool
{
foreach ($this->snippets as $snippet) {
if ($snippet['name'] === $snippetName && $snippet['content'] !== $content) {
return true;
}
}

return false;
}
}
Loading

0 comments on commit fa8b9a0

Please sign in to comment.