From c9b411654ff86ba09bb3034143d81b3c452f3751 Mon Sep 17 00:00:00 2001 From: Julien Mercier-Rojas Date: Fri, 13 Oct 2023 18:18:21 +0200 Subject: [PATCH] Start contact payload parsing --- composer-require-checker.json | 5 +++ composer.json | 18 ++++++---- grumphp.yml | 2 +- src/ContactParser.php | 34 +++++++++++++++++++ src/Model/Client.php | 12 ------- src/Model/Contact.php | 23 +++++++++++++ src/Model/MauticEvent.php | 2 +- src/PayloadParser.php | 15 ++++++-- src/functions.php | 22 ++++++++++++ tests/PayloadParserTest.php | 15 ++++---- .../contact-identified.json | 0 11 files changed, 117 insertions(+), 31 deletions(-) create mode 100644 composer-require-checker.json create mode 100644 src/ContactParser.php delete mode 100644 src/Model/Client.php create mode 100644 src/Model/Contact.php create mode 100644 src/functions.php rename tests/{samples => fixtures}/contact-identified.json (100%) diff --git a/composer-require-checker.json b/composer-require-checker.json new file mode 100644 index 0000000..248ae3c --- /dev/null +++ b/composer-require-checker.json @@ -0,0 +1,5 @@ +{ + "symbol-whitelist": [ + "toNullableDateTime" + ] +} diff --git a/composer.json b/composer.json index 11c3479..bba5ed1 100644 --- a/composer.json +++ b/composer.json @@ -21,23 +21,27 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.34", + "infection/infection": "^0.27.4", + "maglnet/composer-require-checker": "^4.7", + "mockery/mockery": "^1.6", + "phpmd/phpmd": "^2.14", + "phpro/grumphp": "^2.1", "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^1.10", "phpstan/phpstan-deprecation-rules": "^1.1", "phpstan/phpstan-phpunit": "^1.3", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "^10.4", - "symfony/var-dumper": "^6.3", - "phpro/grumphp": "^2.1", - "phpmd/phpmd": "^2.14", - "maglnet/composer-require-checker": "^4.7", - "infection/infection": "^0.27.4", - "roave/security-advisories": "dev-latest" + "roave/security-advisories": "dev-latest", + "symfony/var-dumper": "^6.3" }, "autoload": { "psr-4": { "JeckelLab\\MauticWebhookParser\\": "src" - } + }, + "files": [ + "src/functions.php" + ] }, "autoload-dev": { "psr-4": { diff --git a/grumphp.yml b/grumphp.yml index 3b64a7e..5ddfab4 100644 --- a/grumphp.yml +++ b/grumphp.yml @@ -37,7 +37,7 @@ grumphp: use_grumphp_paths: true composer_require_checker: composer_file: 'composer.json' - config_file: ~ + config_file: 'composer-require-checker.json' ignore_parse_errors: false triggered_by: [ 'composer.json', 'composer.lock', 'src/*.php' ] git_branch_name: diff --git a/src/ContactParser.php b/src/ContactParser.php new file mode 100644 index 0000000..2b4ed0d --- /dev/null +++ b/src/ContactParser.php @@ -0,0 +1,34 @@ + + * Created at: 13/10/2023 + */ + +declare(strict_types=1); + +namespace JeckelLab\MauticWebhookParser; + +use DateTimeImmutable; +use Exception; +use JeckelLab\MauticWebhookParser\Exception\LogicException; +use JeckelLab\MauticWebhookParser\Model\Contact; + +class ContactParser +{ + /** + * @param array $payload + * @return Contact + * @throws Exception|LogicException + */ + public function parse(array $payload): Contact + { + return new Contact( + dateAdded: is_string($payload['dateAdded']) ? new DateTimeImmutable($payload['dateAdded']) : throw new LogicException('Missing dateAdded'), + dateIdentified: is_string($payload['dateIdentified']) ? new DateTimeImmutable($payload['dateIdentified']) : throw new LogicException('Missing dateIdentified'), + dateModified: toNullableDateTime($payload['dateModified'] ?? null), + id: is_int($payload['id']) ? $payload['id'] : throw new LogicException('Missing id'), + isPublished: (bool) $payload['isPublished'], + ); + } +} diff --git a/src/Model/Client.php b/src/Model/Client.php deleted file mode 100644 index 5af6de1..0000000 --- a/src/Model/Client.php +++ /dev/null @@ -1,12 +0,0 @@ - - * Created at: 13/10/2023 - */ - -declare(strict_types=1); - -namespace JeckelLab\MauticWebhookParser\Model; - -class Client {} diff --git a/src/Model/Contact.php b/src/Model/Contact.php new file mode 100644 index 0000000..91acaf9 --- /dev/null +++ b/src/Model/Contact.php @@ -0,0 +1,23 @@ + + * Created at: 13/10/2023 + */ + +declare(strict_types=1); + +namespace JeckelLab\MauticWebhookParser\Model; + +use DateTimeImmutable; + +readonly class Contact +{ + public function __construct( + public DateTimeImmutable $dateAdded, + public DateTimeImmutable $dateIdentified, + public ?DateTimeImmutable $dateModified, + public int $id, + public bool $isPublished, + ) {} +} diff --git a/src/Model/MauticEvent.php b/src/Model/MauticEvent.php index 4af1ab6..195b0c3 100644 --- a/src/Model/MauticEvent.php +++ b/src/Model/MauticEvent.php @@ -16,7 +16,7 @@ { public function __construct( public MauticEventType $eventType, - public Client $client, + public Contact $client, public Lead $lead, public DateTimeImmutable $timestamp ) {} diff --git a/src/PayloadParser.php b/src/PayloadParser.php index 53367c4..94dd998 100644 --- a/src/PayloadParser.php +++ b/src/PayloadParser.php @@ -12,13 +12,16 @@ use DateTimeImmutable; use Exception; use JeckelLab\MauticWebhookParser\Exception\LogicException; -use JeckelLab\MauticWebhookParser\Model\Client; use JeckelLab\MauticWebhookParser\Model\Lead; use JeckelLab\MauticWebhookParser\Model\MauticEvent; use JeckelLab\MauticWebhookParser\ValueObject\MauticEventType; class PayloadParser { + public function __construct( + private readonly ContactParser $clientParser, + ) {} + /** * @param array>> $payload * @return iterable @@ -27,6 +30,9 @@ class PayloadParser public function parse(array $payload): iterable { foreach ($payload as $eventName => $events) { + if ($eventName === 'timestamp') { + continue; + } $event = MauticEventType::from($eventName); foreach ($events as $eventPayload) { yield $this->parseEvent($eventPayload, $event); @@ -43,9 +49,14 @@ public function parseEvent(array $eventPayload, MauticEventType $event): MauticE if (! is_string($eventPayload['timestamp'])) { throw new LogicException('Missing timestamp'); } + if (! is_array($eventPayload['contact'])) { + throw new LogicException('Missing contact'); + } + /** @var array $contactPayload */ + $contactPayload = $eventPayload['contact']; return new MauticEvent( eventType: $event, - client: new Client(), + client: $this->clientParser->parse($contactPayload), lead: new Lead(), timestamp: new DateTimeImmutable($eventPayload['timestamp']) ); diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 0000000..c7aa265 --- /dev/null +++ b/src/functions.php @@ -0,0 +1,22 @@ + + * Created at: 13/10/2023 + */ + +declare(strict_types=1); + +namespace JeckelLab\MauticWebhookParser; + +use DateTimeImmutable; +use Exception; + +function toNullableDateTime(mixed $date): ?DateTimeImmutable +{ + try { + return is_string($date) ? new DateTimeImmutable($date) : null; + } catch (Exception) { + return null; + } +} diff --git a/tests/PayloadParserTest.php b/tests/PayloadParserTest.php index 8077559..03650bc 100644 --- a/tests/PayloadParserTest.php +++ b/tests/PayloadParserTest.php @@ -8,6 +8,7 @@ declare(strict_types=1); +use JeckelLab\MauticWebhookParser\ContactParser; use JeckelLab\MauticWebhookParser\PayloadParser; use JeckelLab\MauticWebhookParser\ValueObject\MauticEventType; use PHPUnit\Framework\TestCase; @@ -16,14 +17,12 @@ class PayloadParserTest extends TestCase { public function testItExtractCorrectEvents(): void { - $parser = new PayloadParser(); - $payload = [ - "mautic.lead_post_save_new" => [ - [ - "timestamp" => "2017-06-19T09:31:18+00:00", - ], - ], - ]; + $parser = new PayloadParser(new ContactParser()); + /** @var string $payloadString */ + $payloadString = file_get_contents(__DIR__ . '/fixtures/contact-identified.json'); + + /** @var array>> $payload */ + $payload = json_decode($payloadString, true); $events = []; foreach($parser->parse($payload) as $event) { $events[] = $event; diff --git a/tests/samples/contact-identified.json b/tests/fixtures/contact-identified.json similarity index 100% rename from tests/samples/contact-identified.json rename to tests/fixtures/contact-identified.json