loburets
7/6/2017 - 5:28 AM

Pagination for Mailgun events API

Pagination for Mailgun events API

<?php

use Mailgun\Mailgun;
use Setting;

/**
 * Pagination for Mailgun events API
 * Retrieves all new (not have retrieved yet) events for each run
 * See main code at processNewEvents()
 *
 * Used, but can be replaced by any implementation:
 *      "mailgun/mailgun-php": "^2.3",
 *      "anlutro/l4-settings": "^0.5.0",
 */
class EmailProcessor
{
    /**
     * Pagination step for api calls, max 300
     * @var int
     */
    private static $step = 300;

    public function sendEmails()
    {
        $this->processNewEmails(function($response) {
            /** @var \Mailgun\Model\Message\ShowResponse $response */
            // any code what you want
            // $response->getBodyPlain();
        });
    }

    /**
     * Make some action with all new inbounded emails
     *
     * @param \Closure $callback
     */
    private function processNewEmails(\Closure $callback)
    {
        $this->processNewEvents(function($event) use($callback) {

            /** @var \Mailgun\Model\Event\Event $event */
            $storage = $event->getStorage();

            if (empty($storage) ||empty($storage['url'])) {
                return;
            }

            $mailgun = Mailgun::create(env('MAILGUN_SECRET'));
            $response = $mailgun->messages()->show($storage['url']);

            $callback($response);
        }, 'stored');
    }

    /**
     * Make some actions with all new events
     *
     * @param \Closure $callback
     * @param string $type
     */
    private function processNewEvents(\Closure $callback, $type)
    {
        $domain = env('MAILGUN_DOMAIN');
        $mailgun = Mailgun::create(env('MAILGUN_SECRET'));
        $latestEventOfPreviousRunSetting = 'latest_processed_mailgun_event_' . $type;
        $latestEventOfPreviousRun = Setting::get($latestEventOfPreviousRunSetting, '0');
        $begin = time();
        $processed = [];
        $latestEventIsWritten = false;

        do {
            $eventsResponse = $mailgun->events()
                ->get(
                    $domain,
                    [
                        'begin' => $begin,
                        'event' => $type,
                        'ascending' => 'no',
                        'limit' => self::$step,
                    ]
                );

            $needToMakeNextApiStep = false;

            foreach ($eventsResponse->getItems() as $event) {

                //already processed last run
                if ($latestEventOfPreviousRun === $event->getId()) {
                    break(2);
                }

                // because after the step of api pagination first part of current batch can contain the same events as previous
                // it depends on crossing of events with the same timestamp (peculiarity of the mailgun API)
                if (in_array($event->getId(), $processed)) {
                    continue;
                }

                $needToMakeNextApiStep = true;
                $processed[] = $event->getId();
                $begin = $event->getTimestamp();

                if (!$latestEventIsWritten) {
                    Setting::set($latestEventOfPreviousRunSetting, $event->getId());
                    Setting::save();
                    $latestEventIsWritten = true;
                }

                $callback($event);
            }
        } while($needToMakeNextApiStep);
    }
}