Skip to content

Commit

Permalink
Add UpdateContactListsSubscriptionCommand (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
lruozzi9 committed Apr 22, 2022
1 parent 9d614f8 commit e89cf90
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 0 deletions.
202 changes: 202 additions & 0 deletions src/Command/UpdateContactListsSubscriptionCommand.php
@@ -0,0 +1,202 @@
<?php

declare(strict_types=1);

namespace Webgriffe\SyliusActiveCampaignPlugin\Command;

use InvalidArgumentException;
use Sylius\Component\Core\Model\CustomerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Webgriffe\SyliusActiveCampaignPlugin\Message\Contact\ContactListsUpdater;
use Webgriffe\SyliusActiveCampaignPlugin\Model\CustomerActiveCampaignAwareInterface;
use Webgriffe\SyliusActiveCampaignPlugin\Repository\ActiveCampaignResourceRepositoryInterface;

final class UpdateContactListsSubscriptionCommand extends Command
{
use LockableTrait;

private const CUSTOMER_ID_ARGUMENT_CODE = 'customer-id';

public const UPDATE_CONTACT_LISTS_SUBSCRIPTION_COMMAND_STOPWATCH_NAME = 'update-contact-lists-subscription-command';

public const ALL_OPTION_CODE = 'all';

/** @psalm-suppress PropertyNotSetInConstructor */
private SymfonyStyle $io;

/** @param ActiveCampaignResourceRepositoryInterface<CustomerInterface> $customerRepository */
public function __construct(
private ActiveCampaignResourceRepositoryInterface $customerRepository,
private MessageBusInterface $messageBus,
private ?string $name = null
) {
parent::__construct($this->name);
}

protected function configure(): void
{
$this
->setHelp($this->getCommandHelp())
->addArgument(self::CUSTOMER_ID_ARGUMENT_CODE, InputArgument::OPTIONAL, 'The identifier id of the customer to which update the subscription to lists status.')
->addOption(self::ALL_OPTION_CODE, 'a', InputOption::VALUE_NONE, 'If set, the command will update the status of list subscriptions for all customers.')
;
}

protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}

protected function interact(InputInterface $input, OutputInterface $output): void
{
if ($input->getOption(self::ALL_OPTION_CODE) === true || $input->getArgument(self::CUSTOMER_ID_ARGUMENT_CODE) !== null) {
return;
}

$this->io->title('Update Contact Lists Subscription Command Interactive Wizard');
$this->io->text([
'If you prefer to not use this interactive wizard, provide the',
'argument required by this command as follows:',
'',
' $ php bin/console ' . (string) $this->name . ' customer-id',
'',
'Now we\'ll ask you for the value of the customer to which update the list subscriptions status.',
]);

// Ask for the Customer ID if it's not defined
/** @var mixed|null $customerId */
$customerId = $input->getArgument(self::CUSTOMER_ID_ARGUMENT_CODE);
if (null === $customerId) {
/** @var mixed $customerId */
$customerId = $this->io->ask('Customer id', null, [$this, 'validateCustomerId']);
$input->setArgument(self::CUSTOMER_ID_ARGUMENT_CODE, $customerId);
}
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$this->lock()) {
$this->io->error('The command is already running in another process.');

return Command::FAILURE;
}
$stopwatch = new Stopwatch();
$stopwatch->start(self::UPDATE_CONTACT_LISTS_SUBSCRIPTION_COMMAND_STOPWATCH_NAME);

/** @var string|int $customerId */
$customerId = $input->getArgument(self::CUSTOMER_ID_ARGUMENT_CODE);
$exportAll = (bool) $input->getOption(self::ALL_OPTION_CODE);

$this->validateInputData($customerId, $exportAll);

if ($exportAll) {
$customersToUpdate = $this->customerRepository->findAllToEnqueue();
} else {
$customer = $this->customerRepository->findOneToEnqueue($customerId);
if ($customer === null) {
throw new InvalidArgumentException(sprintf(
'Unable to find a Customer with id "%s".',
$customerId
));
}
$customersToUpdate = [$customer];
}
if (count($customersToUpdate) === 0) {
$this->io->writeln('No customers founded to update the list subscriptions.');

return Command::SUCCESS;
}
$progressBar = $this->getProgressBar($output, $customersToUpdate);

foreach ($customersToUpdate as $customer) {
if (!$customer instanceof CustomerActiveCampaignAwareInterface) {
continue;
}
/** @var int|string $customerId */
$customerId = $customer->getId();
$this->messageBus->dispatch(new ContactListsUpdater($customerId));

$progressBar->setMessage(sprintf('Customer "%s" enqueued for update the list subscriptions status!', (string) $customerId), 'status');
$progressBar->advance();
}
$progressBar->setMessage(sprintf('Finished to enqueue the %s customers for update the list subscriptions status 🎉', count($customersToUpdate)), 'status');
$progressBar->finish();

$event = $stopwatch->stop(self::UPDATE_CONTACT_LISTS_SUBSCRIPTION_COMMAND_STOPWATCH_NAME);
$this->io->comment(sprintf('Elapsed time: %.2f ms / Consumed memory: %.2f MB', $event->getDuration(), $event->getMemory() / (1024 ** 2)));

$this->release();

return Command::SUCCESS;
}

/**
* @param string|int|null $customerId
*/
private function validateInputData(mixed $customerId, bool $all): void
{
if ($all) {
return;
}
$this->validateCustomerId($customerId);
}

/**
* @param string|int|null $customerId
*
* @return string|int
*/
public function validateCustomerId($customerId)
{
if ($customerId === null || $customerId === '') {
throw new InvalidArgumentException('The Customer id can not be empty.');
}

return $customerId;
}

private function getCommandHelp(): string
{
return <<<'HELP'
The <info>%command.name%</info> command enqueue all Sylius customers to update the list subscriptions status from ActiveCampaign:
<info>php %command.full_name%</info> <comment>customer-id</comment>
By default the command enqueue only one customer. To enqueue all customers,
add the <comment>--all</comment> option:
<info>php %command.full_name%</info> <comment>--all</comment>
If you omit the argument and the option, the command will ask you to
provide the missing customer id:
# command will ask you for the customer id
<info>php %command.full_name%</info>
HELP;
}

private function getProgressBar(OutputInterface $output, array $customersToExport): ProgressBar
{
$progressBar = new ProgressBar($output, count($customersToExport));
$progressBar->setFormat(
"<fg=white;bg=black> %status:-45s%</>\n%current%/%max% [%bar%] %percent:3s%%\n🏁 %estimated:-21s% %memory:21s%"
);
$progressBar->setBarCharacter('<fg=red>⚬</>');
$progressBar->setEmptyBarCharacter('<fg=blue>⚬</>');
$progressBar->setProgressCharacter('🚀');
$progressBar->setRedrawFrequency(10);
$progressBar->setMessage(sprintf('Starting the enqueue for %s customers...', count($customersToExport)), 'status');
$progressBar->start();

return $progressBar;
}
}
9 changes: 9 additions & 0 deletions src/Resources/config/services/command.xml
Expand Up @@ -8,6 +8,7 @@
<parameter key="webgriffe_sylius_active_campaign.command.enqueue_ecommerce_abandoned_cart.name">webgriffe:active-campaign:enqueue-ecommerce-abandoned-cart</parameter>
<parameter key="webgriffe_sylius_active_campaign.command.enqueue_webhook.name">webgriffe:active-campaign:enqueue-webhook</parameter>
<parameter key="webgriffe_sylius_active_campaign.command.enqueue_contact_lists_subscription.name">webgriffe:active-campaign:enqueue-contact-lists-subscription</parameter>
<parameter key="webgriffe_sylius_active_campaign.command.update_contact_lists_subscription.name">webgriffe:active-campaign:update-contact-lists-subscription</parameter>
<parameter key="webgriffe_sylius_active_campaign.command.enqueue_contact_tags_adder.name">webgriffe:active-campaign:enqueue-contact-tags-adder</parameter>
<parameter key="webgriffe_sylius_active_campaign.cart_becomes_abandoned_period">1 day</parameter>
</parameters>
Expand Down Expand Up @@ -63,6 +64,14 @@
<tag name="console.command"/>
</service>

<service id="webgriffe.sylius_active_campaign_plugin.command.update_contact_lists_subscription"
class="Webgriffe\SyliusActiveCampaignPlugin\Command\UpdateContactListsSubscriptionCommand">
<argument type="service" id="sylius.repository.customer"/>
<argument type="service" id="messenger.default_bus"/>
<argument>%webgriffe_sylius_active_campaign.command.update_contact_lists_subscription.name%</argument>
<tag name="console.command"/>
</service>

<service id="webgriffe.sylius_active_campaign_plugin.command.enqueue_contact_tags_adder"
class="Webgriffe\SyliusActiveCampaignPlugin\Command\EnqueueContactTagsAdderCommand">
<argument type="service" id="sylius.repository.customer"/>
Expand Down
@@ -0,0 +1,14 @@
App\Entity\Customer\Customer:
customer_jim:
email: 'jim@email.com'
first_name: 'James'
last_name: 'Rodriguez'
customer_bob:
email: 'bob@email.com'
first_name: 'Bob'
last_name: 'Rodriguez'
activeCampaignId: 32
customer_sam:
email: 'sam@email.com'
first_name: 'Samuel'
last_name: 'Freud'
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

namespace Tests\Webgriffe\SyliusActiveCampaignPlugin\Integration\Command;

use Fidry\AliceDataFixtures\Persistence\PurgeMode;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Symfony\Component\Messenger\Envelope;
use Webgriffe\SyliusActiveCampaignPlugin\Message\Contact\ContactListsUpdater;

final class UpdateContactListsSubscriptionCommandTest extends AbstractCommandTest
{
private const FIXTURE_BASE_DIR = __DIR__ . '/../../DataFixtures/ORM/resources/Command/UpdateContactListsSubscriptionCommandTest';

private CustomerRepositoryInterface $customerRepository;

protected function setUp(): void
{
parent::setUp();
$this->customerRepository = self::getContainer()->get('sylius.repository.customer');

$fixtureLoader = self::getContainer()->get('fidry_alice_data_fixtures.loader.doctrine');
$fixtureLoader->load([
self::FIXTURE_BASE_DIR . '/customers.yaml',
], [], [], PurgeMode::createDeleteMode());
}

public function test_that_it_enqueues_contact_lists_subscription(): void
{
$customer = $this->customerRepository->findOneBy(['email' => 'jim@email.com']);
self::assertNotNull($customer->getId());

$commandTester = $this->executeCommand([
'customer-id' => $customer->getId(),
], []);

self::assertEquals(0, $commandTester->getStatusCode());

$transport = self::getContainer()->get('messenger.transport.main');
/** @var Envelope[] $messages */
$messages = $transport->get();
$this->assertCount(1, $messages);
$message = $messages[0];
$this->assertInstanceOf(ContactListsUpdater::class, $message->getMessage());
$this->assertEquals($customer->getId(), $message->getMessage()->getCustomerId());
}

public function test_that_it_enqueues_contact_lists_subscription_interactively(): void
{
$customer = $this->customerRepository->findOneBy(['email' => 'jim@email.com']);
self::assertNotNull($customer->getId());

$commandTester = $this->executeCommand([], [
$customer->getId(),
]);

self::assertEquals(0, $commandTester->getStatusCode());
$transport = self::getContainer()->get('messenger.transport.main');
/** @var Envelope[] $messages */
$messages = $transport->get();
$this->assertCount(1, $messages);
$message = $messages[0];
$this->assertInstanceOf(ContactListsUpdater::class, $message->getMessage());
$this->assertEquals($customer->getId(), $message->getMessage()->getCustomerId());
}

public function test_that_it_enqueues_all_contacts_lists_subscription(): void
{
$commandTester = $this->executeCommand([
'--all' => true,
]);

self::assertEquals(0, $commandTester->getStatusCode());

$customerJim = $this->customerRepository->findOneBy(['email' => 'jim@email.com']);
$customerBob = $this->customerRepository->findOneBy(['email' => 'bob@email.com']);
$customerSam = $this->customerRepository->findOneBy(['email' => 'sam@email.com']);
$transport = self::getContainer()->get('messenger.transport.main');
/** @var Envelope[] $messages */
$messages = $transport->get();
$this->assertCount(3, $messages);

$message = $messages[0];
$this->assertInstanceOf(ContactListsUpdater::class, $message->getMessage());
$this->assertEquals($customerJim->getId(), $message->getMessage()->getCustomerId());
$message = $messages[1];
$this->assertInstanceOf(ContactListsUpdater::class, $message->getMessage());
$this->assertEquals($customerBob->getId(), $message->getMessage()->getCustomerId());
$message = $messages[2];
$this->assertInstanceOf(ContactListsUpdater::class, $message->getMessage());
$this->assertEquals($customerSam->getId(), $message->getMessage()->getCustomerId());
}

protected function getCommandDefinition(): string
{
return 'webgriffe.sylius_active_campaign_plugin.command.update_contact_lists_subscription';
}
}

0 comments on commit e89cf90

Please sign in to comment.