Skip to content

Commit

Permalink
Add command validate-mail to validate all email addresses for users…
Browse files Browse the repository at this point in the history
… in a given time period.
  • Loading branch information
Brezak authored and rootpd committed Oct 2, 2023
1 parent df24e34 commit 139ac5a
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 3 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Added string error code to the Subscribe APIs to differentiate between different 404 scenarios. remp/web#2263
- Fix Mailer segment provider users acquiring. Provided segment code needs to be processed before fetching users from database. remp/mnt#114
- Fix New template generator form - broken sorting value `after`. If selected, select box was not shown. remp/helpdesk#2073
- Added command `crm:validate-emails` to validate all email addresses for users in a given time period. remp/remp#1026
- You can enable this command in your config.neon if you already defined `crmClient` service:
```
services:
console:
setup:
# Enable only if "crmClient" service is available
- add(Remp\MailerModule\Commands\ValidateCrmEmailsCommand())
```

## Archive

Expand Down
4 changes: 4 additions & 0 deletions Mailer/app/config/config.local.neon.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ services:
# If you're using Beam, you can enable this command to pull conversions from there
# - add(Remp\MailerModule\Commands\ProcessConversionStatsCommand())

# Enable only if "crmClient" service is available
- add Remp\MailerModule\Commands\ValidateCrmEmailsCommand()

# # Internal CRM system. For more details contact Tomas Bella.
# authenticator:
# factory: Remp\MailerModule\Models\Auth\Authenticator
Expand Down Expand Up @@ -61,6 +64,7 @@ services:
#crmClient:
# factory: Remp\MailerModule\Models\Crm\Client(%crm.addr%, %crm.api_token%)


# Setup loggers output
commandsLogger:
setup:
Expand Down
1 change: 0 additions & 1 deletion Mailer/app/config/config.test.neon
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ services:
mailFactory:
setup:
- addMailer(Tests\Feature\Mails\TestMailer())

crmClient:
factory: Remp\MailerModule\Models\Crm\Client(%crm.addr%, %crm.api_token%)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace Remp\MailerModule\Commands;

use DateInterval;
use DateTime;
use Exception;
use Remp\MailerModule\Models\Crm\Client;
use Remp\MailerModule\Models\Crm\UserNotFoundException;
use Remp\MailerModule\Repositories\LogsRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ValidateCrmEmailsCommand extends Command
{
public const COMMAND_NAME = "crm:validate-emails";

public function __construct(
private LogsRepository $mailLogRepository,
private Client $client
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName(self::COMMAND_NAME)
->setDescription('Validates mail sent in the last (by default) 10 minutes.')
->addArgument(
"interval",
InputArgument::OPTIONAL,
"How far back the validation interval should extend. By default 10 minutes.",
'PT10M',
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$client = $this->client;
if (is_null($client)) {
$output->writeln("<error>ERROR</error>: CRM client was not initialized, check your config.local.neon.");
return Command::FAILURE;
}

$interval = $input->getArgument('interval');
try {
$interval = new DateInterval($interval);
} catch (Exception $e) {
$output->writeln("Failed to parse interval <comment>{$interval}</comment>:\n" . $e->getMessage());
return Command::FAILURE;
}

$deliveredAtFrom = (new DateTime())->sub($interval);

$emails = $this->mailLogRepository->getTable()
->select('DISTINCT email')
->where('delivered_at >= ?', $deliveredAtFrom)
->fetchPairs(value: 'email');

$now = new DateTime();

$before = (clone $now)->sub($interval);
$output->writeln(sprintf(
"Validating %d emails, from <info>%s</info> to <info>%s</info>.",
count($emails),
$before->format(\DateTimeInterface::RFC3339),
$now->format(\DateTimeInterface::RFC3339),
));

$client->validateMultipleEmails($emails);
return Command::SUCCESS;
}
}
29 changes: 27 additions & 2 deletions Mailer/extensions/mailer-module/src/Models/Crm/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\RequestOptions;
use Nette\Utils\Json;
use GuzzleHttp\Exception\ClientException;

Expand All @@ -27,7 +28,7 @@ public function validateEmail(string $email): array
{
try {
$response = $this->client->post('api/v1/users/set-email-validated', [
'form_params' => [
RequestOptions::FORM_PARAMS => [
'email' => $email,
],
]);
Expand All @@ -40,7 +41,31 @@ public function validateEmail(string $email): array
if (isset($body['code']) && $body['code'] === 'email_not_found') {
throw new UserNotFoundException("Unable to find email: {$clientException->getMessage()}");
}
throw new Exception("unable to confirm CRM user: {$clientException->getMessage()}");
} catch (ServerException $serverException) {
throw new Exception("unable to confirm CRM user: {$serverException->getMessage()}");
}
}

/**
* @param string[] $emails
**/
public function validateMultipleEmails(array $emails): mixed
{
// An empty post request would just waste cpu cycles
if (count($emails) === 0) {
return [];
}

try {
$response = $this->client->post('api/v2/users/set-email-validated', [
RequestOptions::JSON => ['emails' => $emails]
]);

return Json::decode($response->getBody()->getContents(), Json::FORCE_ARRAY);
} catch (ConnectException $connectException) {
throw new Exception("could not connect to CRM: {$connectException->getMessage()}");
} catch (ClientException $clientException) {
throw new Exception("unable to confirm CRM user: {$clientException->getMessage()}");
} catch (ServerException $serverException) {
throw new Exception("unable to confirm CRM user: {$serverException->getMessage()}");
Expand All @@ -51,7 +76,7 @@ public function userTouch(int $userId): bool
{
try {
$response = $this->client->get('api/v1/users/touch', [
'query' => [
RequestOptions::QUERY => [
'id' => $userId,
]
]);
Expand Down

0 comments on commit 139ac5a

Please sign in to comment.