Skip to content

Commit

Permalink
Merge pull request #226 from utmsigep/esp-webhooks
Browse files Browse the repository at this point in the history
ESP Webhook support
  • Loading branch information
stephenyeargin committed Mar 13, 2021
2 parents 9f573f9 + 34c001c commit ca0eb71
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 41 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ DATABASE_URL=mysql://db_user:[email protected]:3306/db_name
###< doctrine/doctrine-bundle ###

###> symfony/mailer ###
# MAILER_DSN=smtp://localhost
MAILER_DSN=smtp://localhost
###< symfony/mailer ###

APP_NAME="Member Directory"
APP_BASE_URI=
APP_LOGO=
APP_EMAIL_TO=
APP_EMAIL_FROM=
Expand Down
1 change: 1 addition & 0 deletions config/packages/routing.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
framework:
router:
default_uri: '%env(resolve:APP_BASE_URI)%'
strict_requirements: ~
utf8: true
92 changes: 92 additions & 0 deletions src/Command/EspWebhookCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace App\Command;

use App\Service\EmailService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
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;

class EspWebhookCommand extends Command
{
protected static $defaultName = 'app:esp:webhook';
protected static $defaultDescription = 'Manage ESP webhooks.';

protected $emailService;

public function __construct(EmailService $emailService)
{
parent::__construct();
$this->emailService = $emailService;
}

protected function configure()
{
$this
->setDescription(self::$defaultDescription)
->addArgument('action', InputArgument::REQUIRED, 'Action to take (list, create, delete)')
->addOption('webhook-id', null, InputOption::VALUE_OPTIONAL, 'The UUID for the webhook.')
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

if (!$this->emailService->isConfigured()) {
$io->error('Email Service not configured.');
return Command::FAILURE;
}

switch ($input->getArgument('action')) {
case 'list':
$webhooks = $this->emailService->getWebhooks();
if (!is_array($webhooks) || count($webhooks) == 0) {
$io->info('No webhooks configured.');
return Command::SUCCESS;
}
$table = new Table($output);
$rows = [];
foreach ($webhooks as $webhook) {
$rows[] = [
$webhook->WebhookID,
$webhook->Url,
implode(', ', $webhook->Events),
$webhook->Status,
mb_strtoupper($webhook->PayloadFormat)
];
}
$table->setHeaders(['Webhook ID', 'URL', 'Events', 'Status', 'Format']);
$table->setRows($rows);
$table->render();
break;
case 'create':
$webhook = $this->emailService->createWebhook();
if (!$webhook) {
$io->error('Unable to create webhook.');
return Command::FAILURE;
}
$io->success(sprintf('Created webhook: %s', $webhook));
break;
case 'delete':
$webhookId = $input->getOption('webhook-id');
if (!$webhookId) {
$io->error('You must provide a --webhook-id=somestring');
return Command::FAILURE;
}
$result = $this->emailService->deleteWebhook($webhookId);
if (!$result) {
$io->error(sprintf('Unable to delete webhook: %s', $webhookId));
return Command::FAILURE;
}
$io->success(sprintf('Deleted webhook: %s', $webhookId));
break;
}

return Command::SUCCESS;
}
}
23 changes: 3 additions & 20 deletions src/Controller/UpdateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

namespace App\Controller;

use App\Service\EmailService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mailer\MailerInterface;

use App\Entity\Member;
use App\Form\MemberUpdateType;
Expand Down Expand Up @@ -35,7 +33,7 @@ public function updateFromQueryString(Request $request)
/**
* @Route("/update-my-info/{externalIdentifier}/{updateToken}", name="self_service_update")
*/
public function update(Request $request, MailerInterface $mailer)
public function update(Request $request, EmailService $emailService)
{
$member = $this->getDoctrine()->getRepository(Member::class)->findOneBy([
'externalIdentifier' => $request->get('externalIdentifier')
Expand All @@ -55,25 +53,10 @@ public function update(Request $request, MailerInterface $mailer)
$member = $form->getData();
// If form is submitted, member is no longer "lost"
$member->setIsLost(false);
// Set headers for grouping in transactional email reporting
$headers = new Headers();
$headers->addTextHeader('X-Cmail-GroupName', 'Member Record Update');
$headers->addTextHeader('X-MC-Tags', 'Member Record Update');
$message = new TemplatedEmail($headers);
$message
->to($this->getParameter('app.email.to'))
->from($this->getParameter('app.email.from'))
->subject(sprintf('Member Record Update: %s', $member->getDisplayName()))
->htmlTemplate('update/email_update.html.twig')
->context(['member' => $member])
;
if ($member->getPrimaryEmail()) {
$message->replyTo($member->getPrimaryEmail());
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($member);
$entityManager->flush();
$mailer->send($message);
$emailService->sendMemberUpdate($member);
return $this->render('update/confirmation.html.twig');
}

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

namespace App\Controller;

use App\Service\EmailService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class WebhookController extends AbstractController
{
/**
* @Route("/webhook", name="webhook")
*/
public function index(Request $request): Response
{
return $this->json([
'status' => 200,
'title' => 'Success',
'message' => 'Webhooks are available.'
]);
}

/**
* @Route("/webhook/email-service", name="webhook_email_service", methods={"POST"})
*/
public function emailServiceWebhook(Request $request, EmailService $emailService): Response
{
// Fail if not configured
if (!$emailService->isConfigured()) {
return $this->json([
'status' => 500,
'title' => 'Internal server error',
'details' => 'Email service not configured.'
], 500);
}

// Fail if token is missing or mismatched
if (!$request->get('token') ||
$request->get('token') != $emailService->getWebhookToken()
) {
return $this->json([
'status' => 403,
'title' => 'Access denied',
'details' => 'Invalid credentials.'
], 403);
}

// Process payload
try {
$output = $emailService->processWebhookBody($request->getContent());
return $this->json([
'status' => 200,
'title' => 'success',
'details' => 'Processed webhook.',
'extra' => $output
]);
} catch (\Exception $e) {
return $this->json([
'status' => 'error',
'title' => 'Internal server error',
'message' => $e->getMessage()
], 500);
}
}


}
Loading

0 comments on commit ca0eb71

Please sign in to comment.