Page MenuHomeDevCentral

D639.id1599.diff
No OneTemporary

D639.id1599.diff

diff --git a/.arcconfig b/.arcconfig
new file mode 100644
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,5 @@
+{
+ "repository.callsign": "KMAILGUN",
+ "phabricator.uri": "https://devcentral.nasqueron.org",
+ "unit.engine": "PhpunitTestEngine"
+}
diff --git a/.arclint b/.arclint
new file mode 100644
--- /dev/null
+++ b/.arclint
@@ -0,0 +1,41 @@
+{
+ "exclude": [
+ "(^vendor/)"
+ ],
+ "linters": {
+ "chmod": {
+ "type": "chmod"
+ },
+ "filename": {
+ "type": "filename"
+ },
+ "json": {
+ "type": "json",
+ "include": [
+ "(^\\.arcconfig$)",
+ "(^\\.arclint$)",
+ "(\\.json$)"
+ ]
+ },
+ "merge-conflict": {
+ "type": "merge-conflict"
+ },
+ "php": {
+ "type": "php",
+ "include": "(\\.php$)"
+ },
+ "phpcs": {
+ "type": "phpcs",
+ "bin": "vendor/bin/phpcs",
+ "phpcs.standard": "PSR1",
+ "include": "(^app/.*\\.php$)"
+ },
+ "spelling": {
+ "type": "spelling"
+ },
+ "xml": {
+ "type": "xml",
+ "include": "(\\.xml$)"
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor/
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+This project adheres to [semantic versioning](http://semver.org/).
+
+## [0.0.1] - 2016-09-01
+### Added
+- Initial version
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# keruald/mailgun
+
+Mailgun API client
+
+Allow to retrieve a stored message on Mailgun.
diff --git a/composer.json b/composer.json
new file mode 100644
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "keruald/mailgun",
+ "description": "Mailgun API client to retrieve a mail",
+ "license": "BSD-2-Clause",
+ "authors": [
+ {
+ "name": "Sébastien Santoro",
+ "email": "dereckson@espace-win.org"
+ },
+ {
+ "name": "Yassine Hadj Messaoud",
+ "email": "modepadu95@riseup.net"
+ }
+ ],
+ "keywords": [
+ "keruald",
+ "Mailgun"
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.net/wolfplex",
+ "issues": "https://devcentral.nasqueron.org"
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "guzzlehttp/psr7": "~1.3",
+ "guzzlehttp/guzzle": "~6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "5.0.*",
+ "psy/psysh": "dev-master",
+ "squizlabs/php_codesniffer": "*",
+ "mockery/mockery": "^0.9.5"
+ },
+ "autoload": {
+ "psr-4": {
+ "Keruald\\Mailgun\\": "src/",
+ "Keruald\\Mailgun\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "test": "phpunit tests"
+ }
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<phpunit bootstrap="./vendor/autoload.php">
+ <testsuites>
+ <testsuite name="Library test suite">
+ <directory>./tests/</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist>
+ <directory suffix=".php">src/</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/src/MailgunMessage.php b/src/MailgunMessage.php
new file mode 100644
--- /dev/null
+++ b/src/MailgunMessage.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace Keruald\Mailgun;
+
+use GuzzleHttp\Client;
+
+use InvalidArgumentException;
+
+class MailgunMessage {
+
+ ///
+ /// Private properties
+ ///
+
+ /**
+ * @var stdClass
+ */
+ private $message = null;
+
+ /**
+ * @var HttpClient
+ */
+ private $client;
+
+ /**
+ * @var string
+ */
+ private $url;
+
+ /**
+ * @var string
+ */
+ private $key;
+
+ ///
+ /// Constructor
+ ///
+
+ /**
+ * Initializes a new instance of the MailgunMessage object.
+ *
+ * @param \GuzzleHttp\Client $client HTTP client
+ * @param string $url The URL of the message to fetch.
+ * @param string $key The API key to use to fetch the message.
+ */
+ public function __construct (Client $client, $url, $key) {
+ $this->client = $client;
+ $this->url = $url;
+ $this->key = $key;
+ }
+
+ /**
+ * Initializes a new instance of the MailgunMessage object from a payload.
+ *
+ * @param \GuzzleHttp\Client $client HTTP client
+ * @param object $payload The payload fired by MailGun routing API
+ * @param string $key The API key to use to fetch the message.
+ */
+ public static function loadFromEventPayload (Client $client, $payload, $key) {
+ $url = self::extractUrlFromEventPayload($payload);
+ return new self($client, $url, $key);
+ }
+
+ ///
+ /// Public API methods
+ ///
+
+ /**
+ * Gets a JSON representation of a mail through Mailgun API.
+ *
+ * @return stdClass
+ */
+ public function get () {
+ if ($this->message === null) {
+ $this->fetch();
+ }
+
+ return $this->message;
+ }
+
+ ///
+ /// Helper methods to fetch a message from Mailgun.
+ ///
+
+ /**
+ * Fetches the message through Mailgun API.
+ *
+ * If successful, fills the message property.
+ *
+ * @throws \RuntimeException when HTTP status code isn't 200
+ */
+ private function fetch () {
+ $response = $this->client->request(
+ 'GET',
+ $this->url,
+ $this->getHttpOptions()
+ );
+
+ if ($response->getStatusCode() !== 200) {
+ throw new \RuntimeException("Can't reach Mailgun API endpoint: $url");
+ }
+
+ $result = $response->getBody();
+ $this->message = json_decode($result);
+ }
+
+ /**
+ * @return array
+ */
+ private function getHttpOptions () {
+ return [
+ 'auth' => [
+ 'api',
+ $this->key
+ ]
+ ];
+ }
+
+ ///
+ /// Helper methods to process payload
+ ///
+
+ /**
+ * Extracts the MailGun URL to retrieve a stored message.
+ *
+ * @return string
+ * @throw \InvalidArgumentException if payload doesn't contain URL where expected.
+ */
+ private static function extractUrlFromEventPayload ($payload) {
+ if (!isset($payload->storage->url)) {
+ throw new InvalidArgumentException("The payload should be an object with a storage.url property.");
+ }
+ return $payload->storage->url;
+ }
+
+}
diff --git a/src/MailgunMessageFactory.php b/src/MailgunMessageFactory.php
new file mode 100644
--- /dev/null
+++ b/src/MailgunMessageFactory.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Keruald\Mailgun;
+
+use GuzzleHttp\Client;
+
+use stdClass;
+
+/**
+ * Allows to build several MailgunMessage instances
+ * with the same HTTP client and API key.
+ */
+class MailgunMessageFactory {
+
+ ///
+ /// Private members, to pass to MailgunMessage instances
+ ///
+
+ /**
+ * @var string
+ */
+ private $key;
+
+ /**
+ * @var HttpClient
+ */
+ private $client;
+
+ ///
+ /// Constructor
+ ///
+
+ /**
+ * Initializes a new instance of the MailgunMessageFactory object.
+ *
+ * @param \GuzzleHttp\Client $client HTTP client
+ * @param string $key The API key to use to fetch the message.
+ */
+ public function __construct (Client $client, $key) {
+ $this->client = $client;
+ $this->key = $key;
+ }
+
+ ///
+ /// Builder
+ ///
+
+ /**
+ * @param string $url The Mailgun URL of the message to fetch.
+ * @return MailgunMessage
+ */
+ public function getMessage ($url) {
+ return new MailgunMessage($this->client, $url, $this->key);
+ }
+
+ /**
+ * Gets a JSON representation of a mail.
+ *
+ * @param string $url The Mailgun URL of the message to fetch.
+ * @return object
+ */
+ public function fetchMessage ($url) {
+ return $this->getMessage($url)->get();
+ }
+
+ /**
+ * @param stdClass $payload The payload fired by MailGun routing API
+ * @return MailgunMessage
+ */
+ public function getMessageFromPayload (stdClass $payload) {
+ return MailgunMessage::loadFromEventPayload(
+ $this->client, $payload, $this->key
+ );
+ }
+
+ /**
+ * Gets a JSON representation of a mail.
+ *
+ * @param stdClass $payload The payload fired by MailGun routing API
+ * @return object
+ */
+ public function fetchMessageFromPayload (stdClass $payload) {
+ return $this->getMessageFromPayload($payload)->get();
+ }
+
+}
diff --git a/tests/MailgunMessageFactoryTest.php b/tests/MailgunMessageFactoryTest.php
new file mode 100644
--- /dev/null
+++ b/tests/MailgunMessageFactoryTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Keruald\Mailgun\Tests;
+
+use Keruald\Mailgun\MailgunMessageFactory;
+use Keruald\Mailgun\MailgunMessage;
+
+use PHPUnit_Framework_TestCase as TestCase;
+
+use stdClass;
+
+class MailgunMessageFactoryTest extends TestCase {
+
+ use WithMockHttpClient;
+
+ /**
+ * @var \Keruald\Mailgun\MailgunMessageFactory
+ */
+ private $factory;
+
+ public function setUp () {
+ $client = self::mockHttpClient();
+ $this->factory = new MailgunMessageFactory($client, "0000");
+ }
+
+ public function testGetMessage () {
+ $message = $this->factory->getMessage("http://api/somemessage");
+ $this->assertInstanceOf(MailgunMessage::class, $message);
+ }
+
+ public function testGetMessageFromPayload () {
+ $payload = self::mockEventPayload();
+ $message = $this->factory->getMessageFromPayload($payload);
+ $this->assertInstanceOf(MailgunMessage::class, $message);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testGetMessageFromPayloadThrowsExceptionWhenPayloadDoesNotContainUrlInformation () {
+ $this->factory->getMessageFromPayload(new stdClass);
+ }
+
+ public function testFetchMessage () {
+ $message = $this->factory->fetchMessage("http://api/somemessage");
+ $this->assertInstanceOf("stdClass", $message);
+ }
+
+ public function testFetchMessageFromPayload () {
+ $payload = self::mockEventPayload();
+ $message = $this->factory->fetchMessageFromPayload($payload);
+ $this->assertInstanceOf("stdClass", $message);
+ }
+
+}
diff --git a/tests/WithMockHttpClient.php b/tests/WithMockHttpClient.php
new file mode 100644
--- /dev/null
+++ b/tests/WithMockHttpClient.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Keruald\Mailgun\Tests;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Handler\MockHandler;
+use GuzzleHttp\Psr7\Response;
+
+trait WithMockHttpClient {
+
+ /**
+ * @return \GuzzleHttp\
+ */
+ public function mockHttpClient () {
+ $body = $this->mockHttpClientResponseBody();
+ return self::mockHttpClientWithCustomResponse(200, $body);
+ }
+
+ /**
+ * @return \GuzzleHttp\Client
+ */
+ public function mockHttpClientWithCustomResponse ($code, $body) {
+ $handler = self::getCustomMockHttpClientHandler($code, $body);
+ return new Client(['handler' => $handler]);
+ }
+
+ /**
+ * @return stdClass
+ */
+ public function mockEventPayload () {
+ return json_decode(file_get_contents(__DIR__ . '/payload.json'));
+ }
+
+ ///
+ /// Mock helper methods
+ ///
+
+ /**
+ * @return \GuzzleHttp\HandlerStack
+ */
+ protected static function getCustomMockHttpClientHandler ($code, $body, $headers = []) {
+ return HandlerStack::create(new MockHandler([
+ new Response($code, $headers, $body),
+ ]));
+ }
+
+ /**
+ * @return string
+ */
+ protected static function mockHttpClientResponseBody () {
+ return file_get_contents(__DIR__ . '/response.json');
+ }
+
+}
diff --git a/tests/payload.json b/tests/payload.json
new file mode 100644
--- /dev/null
+++ b/tests/payload.json
@@ -0,0 +1,40 @@
+{
+ "tags": [],
+ "timestamp": 1472676730.131279,
+ "storage": {
+ "url": "https://so.api.mailgun.net/v3/domains/notifications.domain.tld/messages/somehash",
+ "key": "somekey"
+ },
+ "envelope": {
+ "sender": "no-reply@notify.docker.com",
+ "transport": "smtp",
+ "targets": "dockerhub@notifications.domain.tld"
+ },
+ "recipient-domain": "notifications.domain.tld",
+ "method": "smtp",
+ "campaigns": [],
+ "user-variables": {},
+ "flags": {
+ "is-routed": null,
+ "is-authenticated": false,
+ "is-system-test": false,
+ "is-test-mode": false
+ },
+ "log-level": "info",
+ "id": "te1cKBxQTJWxpalPRcnmBw",
+ "message": {
+ "headers": {
+ "to": "dockerhub@notifications.domain.tld",
+ "message-id": "CAKg6iAHgQ4e8etV=gi4pbBueeA-o0Qsv02u1Cp9ZKTH8-tL9xw@mail.gmail.com",
+ "from": "no-reply@notify.docker.com",
+ "subject": "There's been an issue with your automated build"
+ },
+ "attachments": [],
+ "recipients": [
+ "dockerhub@notifications.domain.tld"
+ ],
+ "size": 2674
+ },
+ "recipient": "dockerhub@notifications.domain.tld",
+ "event": "accepted"
+}
diff --git a/tests/response.json b/tests/response.json
new file mode 100644
--- /dev/null
+++ b/tests/response.json
@@ -0,0 +1,92 @@
+{
+ "Received": "by 10.159.36.111 with HTTP; Wed, 31 Aug 2016 13:52:08 -0700 (PDT)",
+ "stripped-signature": "",
+ "From": "Docker Notify <no-reply@notify.docker.com>",
+ "X-Envelope-From": "<no-reply@notify.docker.com>",
+ "recipients": "dockerhub@notifications.domain.tld",
+ "X-Google-Dkim-Signature": "v=1; a=rsa-sha256; c=relaxed\/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=mh8Y6W2HqOMo2y75revb4uvzUOKsWoeabsuU5I2tA6c=; b=XjAi2eyhoKNqT8cY4CSyWDd8YBjtSdvNPCO43SF0kzAUPzH7EI3gc6dqOMmJwXHqgA z5EgS4NaJarYrTIDmV3+w42mpccqv+bqVmFbXWsGSSNEMnW8gaDlVq0ckSwHkbVmogOm Ne447o8wbI5mOSxchuPfRHseD0MN4KWmRiHnpWl7aawA4ADDqB79cSJzn8Z2LCwM3uyw CuoX3UngLt3UURf45xm\/emIsNgdyBkfp6Tdd3BaLHBl3KeeLRJELIu8W+PFYtOogV16T eR6FtOkpXZwT3j9gZ6WbYTzd93uw+j9xamWhvTo4RQY\/uYZVjFsZMcYaJVGnxQPUam7Q Qdyw==",
+ "To": "dockerhub@notifications.domain.tld",
+ "message-headers": [
+ [
+ "X-Mailgun-Incoming",
+ "Yes"
+ ],
+ [
+ "X-Envelope-From",
+ "<no-reply@notify.docker.com>"
+ ],
+ [
+ "Received",
+ "from mail-ua0-f180.google.com (mail-ua0-f180.google.com [209.85.217.180]) by mxa.mailgun.org with ESMTP id 57c7437a.7fa164412130-in3; Wed, 31 Aug 2016 20:52:10 -0000 (UTC)"
+ ],
+ [
+ "Received",
+ "by mail-ua0-f180.google.com with SMTP id l94so110731231ual.0 for <dockerhub@notifications.domain.tld>; Wed, 31 Aug 2016 13:52:09 -0700 (PDT)"
+ ],
+ [
+ "Dkim-Signature",
+ "v=1; a=rsa-sha256; c=relaxed\/relaxed; d=espace-win-org.20150623.gappssmtp.com; s=20150623; h=mime-version:from:date:message-id:subject:to; bh=mh8Y6W2HqOMo2y75revb4uvzUOKsWoeabsuU5I2tA6c=; b=Ojs\/SXj3CLGS8ROxh2xUVkXDsYl2mnm0ICZ3FXvMOeny3d5RhwJNKOP7SN+9uEOabQ Jb3RRfa2lslO7KGI\/Te\/y0+JaC7ZHFv3DwEUY\/12hULQLgX06Kn7Af6W7jWsJinvtfmg CFrAOmCxkS16yURsVsgDsrMDbmm6myAVSUKqnvNK7zWG1\/FKJMlH0FRtJFs1C5AuIOKq MLODcNY8bIP0RY9ipr116MAs60WoeCAM4NbSsJacjC4Rbc8RMdWH0Uc2t9C7wlGTIhcS f2V4TIVA1ijvt1R7ePOfrECVyYf\/xHvmQPgAdJ+\/iwv70isSwfgsUW\/V8VEG4HD1LhEo oHqA=="
+ ],
+ [
+ "X-Google-Dkim-Signature",
+ "v=1; a=rsa-sha256; c=relaxed\/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=mh8Y6W2HqOMo2y75revb4uvzUOKsWoeabsuU5I2tA6c=; b=XjAi2eyhoKNqT8cY4CSyWDd8YBjtSdvNPCO43SF0kzAUPzH7EI3gc6dqOMmJwXHqgA z5EgS4NaJarYrTIDmV3+w42mpccqv+bqVmFbXWsGSSNEMnW8gaDlVq0ckSwHkbVmogOm Ne447o8wbI5mOSxchuPfRHseD0MN4KWmRiHnpWl7aawA4ADDqB79cSJzn8Z2LCwM3uyw CuoX3UngLt3UURf45xm\/emIsNgdyBkfp6Tdd3BaLHBl3KeeLRJELIu8W+PFYtOogV16T eR6FtOkpXZwT3j9gZ6WbYTzd93uw+j9xamWhvTo4RQY\/uYZVjFsZMcYaJVGnxQPUam7Q Qdyw=="
+ ],
+ [
+ "X-Gm-Message-State",
+ "AE9vXwPy42uZleQ\/IOiMoGMd57W\/+fLn5kLJ7VAIgP3r2dslZxtBKwG6QXocW99ySghDDOL\/RqP7Al9CDS3Xsw=="
+ ],
+ [
+ "X-Received",
+ "by 10.31.114.140 with SMTP id n134mr6999753vkc.54.1472676729180; Wed, 31 Aug 2016 13:52:09 -0700 (PDT)"
+ ],
+ [
+ "Mime-Version",
+ "1.0"
+ ],
+ [
+ "Received",
+ "by 10.159.36.111 with HTTP; Wed, 31 Aug 2016 13:52:08 -0700 (PDT)"
+ ],
+ [
+ "From",
+ "Docker Notify <no-reply@notify.docker.com>"
+ ],
+ [
+ "Date",
+ "Wed, 31 Aug 2016 22:52:08 +0200"
+ ],
+ [
+ "Message-Id",
+ "<CAKg6iAHgQ4e8etV=gi4pbBueeA-o0Qsv02u1Cp9ZKTH8-tL9xw@mail.gmail.com>"
+ ],
+ [
+ "Subject",
+ "There's been an issue with your automated build"
+ ],
+ [
+ "To",
+ "dockerhub@notifications.domain.tld"
+ ],
+ [
+ "Content-Type",
+ "text\/plain; charset=\"UTF-8\""
+ ]
+ ],
+ "Dkim-Signature": "v=1; a=rsa-sha256; c=relaxed\/relaxed; d=espace-win-org.20150623.gappssmtp.com; s=20150623; h=mime-version:from:date:message-id:subject:to; bh=mh8Y6W2HqOMo2y75revb4uvzUOKsWoeabsuU5I2tA6c=; b=Ojs\/SXj3CLGS8ROxh2xUVkXDsYl2mnm0ICZ3FXvMOeny3d5RhwJNKOP7SN+9uEOabQ Jb3RRfa2lslO7KGI\/Te\/y0+JaC7ZHFv3DwEUY\/12hULQLgX06Kn7Af6W7jWsJinvtfmg CFrAOmCxkS16yURsVsgDsrMDbmm6myAVSUKqnvNK7zWG1\/FKJMlH0FRtJFs1C5AuIOKq MLODcNY8bIP0RY9ipr116MAs60WoeCAM4NbSsJacjC4Rbc8RMdWH0Uc2t9C7wlGTIhcS f2V4TIVA1ijvt1R7ePOfrECVyYf\/xHvmQPgAdJ+\/iwv70isSwfgsUW\/V8VEG4HD1LhEo oHqA==",
+ "content-id-map": {},
+ "subject": "There's been an issue with your automated build",
+ "stripped-html": "<p>Hi Jonh Doe,\r\n\r\nThere seems to have been an issue with your Automated Build\r\n\"acme\/foo\" (VCS repository: acme\/docker-foo)\r\nduring the build step.\r\nYou can find more information on\r\nhttps:\/\/hub.docker.com\/r\/acme\/foo\/builds\/bgrvkrh3dvptvcu3wime8uj\/<\/p>",
+ "from": "Docker Notify <no-reply@notify.docker.com>",
+ "sender": "no-reply@notify.docker.com",
+ "stripped-text": "Hi Jonh Doe,\r\n\r\nThere seems to have been an issue with your Automated Build\r\n\"acme\/foo\" (VCS repository: acme\/docker-foo)\r\nduring the build step.\r\nYou can find more information on\r\nhttps:\/\/hub.docker.com\/r\/acme\/foo\/builds\/bgrvkrh3dvptvcu3wime8uj\/",
+ "X-Mailgun-Incoming": "Yes",
+ "X-Received": "by 10.31.114.140 with SMTP id n134mr6999753vkc.54.1472676729180; Wed, 31 Aug 2016 13:52:09 -0700 (PDT)",
+ "X-Gm-Message-State": "AE9vXwPy42uZleQ\/IOiMoGMd57W\/+fLn5kLJ7VAIgP3r2dslZxtBKwG6QXocW99ySghDDOL\/RqP7Al9CDS3Xsw==",
+ "attachments": [],
+ "Mime-Version": "1.0",
+ "Date": "Wed, 31 Aug 2016 22:52:08 +0200",
+ "Message-Id": "<CAKg6iAHgQ4e8etV=gi4pbBueeA-o0Qsv02u1Cp9ZKTH8-tL9xw@mail.gmail.com>",
+ "Content-Type": "text\/plain; charset=\"UTF-8\"",
+ "body-plain": "Hi Jonh Doe,\r\n\r\nThere seems to have been an issue with your Automated Build\r\n\"acme\/foo\" (VCS repository: acme\/docker-foo)\r\nduring the build step.\r\nYou can find more information on\r\nhttps:\/\/hub.docker.com\/r\/acme\/foo\/builds\/abcdefg123456\/",
+ "Subject": "There's been an issue with your automated build"
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 13:00 (7 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2262525
Default Alt Text
D639.id1599.diff (19 KB)

Event Timeline