diff --git a/.gitignore b/.gitignore
index dddbbe4..1785fa6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ composer.lock
nbproject/*
.idea
phpunit.phar
+modd.conf
diff --git a/.travis.yml b/.travis.yml
index fadbf8c..b50b894 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: php
-
+
php:
- 5.5
- 5.6
@@ -11,6 +11,9 @@ before_install:
install:
- travis_retry composer install
+ - travis_retry composer require --dev 'phpunit/php-code-coverage=^2.2.4'
script:
- - phpunit
\ No newline at end of file
+ - phpunit -c phpunit.xml.dist --testsuite unit --coverage-text
+ - phpunit -c phpunit.xml.dist --testsuite functional --coverage-text
+ - '[[ "${TRAVIS_PULL_REQUEST}" == "false" ]] && ( phpunit -c phpunit.xml.dist --testsuite integration --coverage-text ) || ( echo "Testing PR - No integration tests available")'
diff --git a/composer.json b/composer.json
index 0381a24..c429c5d 100644
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,7 @@
"webmozart/assert": "^1.1"
},
"require-dev": {
- "phpunit/phpunit": "~4.6",
+ "phpunit/phpunit": "~4.8",
"php-http/guzzle6-adapter": "^1.0"
},
"autoload": {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 8707107..bbe28c2 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,7 +13,26 @@
tests
+
+
+ tests
+ tests/Integration
+ test/Functional
+
+
+
+ tests/Integration
+
+
+
+ tests/Functional
+
+
+
+ src
+
+
diff --git a/src/Mailgun/Api/AbstractApi.php b/src/Mailgun/Api/AbstractApi.php
index ec4b08e..88ceed4 100644
--- a/src/Mailgun/Api/AbstractApi.php
+++ b/src/Mailgun/Api/AbstractApi.php
@@ -4,10 +4,15 @@ namespace Mailgun\Api;
use Http\Client\Common\HttpMethodsClient;
use Http\Client\HttpClient;
-use Http\Message\RequestFactory;
-use Mailgun\Exception\HttpServerException;
use Http\Client\Exception as HttplugException;
+use Http\Discovery\MessageFactoryDiscovery;
+use Http\Discovery\StreamFactoryDiscovery;
+use Http\Message\RequestFactory;
+use Http\Message\MultipartStream\MultipartStreamBuilder;
+use Mailgun\Assert;
+use Mailgun\Exception\HttpServerException;
use Mailgun\Serializer\ResponseDeserializer;
+use Mailgun\Resource\Api\SimpleResponse;
use Psr\Http\Message\ResponseInterface;
/**
@@ -38,6 +43,25 @@ abstract class AbstractApi
$this->serializer = $serializer;
}
+ /**
+ * Attempts to safely deserialize the response into the given class.
+ * If the HTTP return code != 200, deserializes into SimpleResponse::class
+ * to contain the error message and any other information provided.
+ *
+ * @param ResponseInterface $response
+ * @param string $className
+ *
+ * @return $class|SimpleResponse
+ */
+ protected function safeDeserialize(ResponseInterface $response, $className)
+ {
+ if ($response->getStatusCode() !== 200) {
+ return $this->serializer->deserialize($response, SimpleResponse::class);
+ } else {
+ return $this->serializer->deserialize($response, $className);
+ }
+ }
+
/**
* Send a GET request with query parameters.
*
@@ -76,6 +100,20 @@ abstract class AbstractApi
return $this->postRaw($path, $this->createJsonBody($parameters), $requestHeaders);
}
+ /**
+ * Send a POST request with parameters encoded as multipart-stream form data.
+ *
+ * @param string $path Request path.
+ * @param array $parameters POST parameters to be mutipart-stream-encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return ResponseInterface
+ */
+ protected function postMultipart($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return $this->doMultipart('POST', $path, $parameters, $requestHeaders);
+ }
+
/**
* Send a POST request with raw data.
*
@@ -116,6 +154,20 @@ abstract class AbstractApi
return $response;
}
+ /**
+ * Send a PUT request with parameters encoded as multipart-stream form data.
+ *
+ * @param string $path Request path.
+ * @param array $parameters PUT parameters to be mutipart-stream-encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return ResponseInterface
+ */
+ protected function putMultipart($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return $this->doMultipart('PUT', $path, $parameters, $requestHeaders);
+ }
+
/**
* Send a DELETE request with JSON-encoded parameters.
*
@@ -136,6 +188,60 @@ abstract class AbstractApi
return $response;
}
+ /**
+ * Send a DELETE request with parameters encoded as multipart-stream form data.
+ *
+ * @param string $path Request path.
+ * @param array $parameters DELETE parameters to be mutipart-stream-encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return ResponseInterface
+ */
+ protected function deleteMultipart($path, array $parameters = [], array $requestHeaders = [])
+ {
+ return $this->doMultipart('DELETE', $path, $parameters, $requestHeaders);
+ }
+
+ /**
+ * Send a request with parameters encoded as multipart-stream form data.
+ *
+ * @param string $type Request type. (POST, PUT, etc.)
+ * @param string $path Request path.
+ * @param array $parameters POST parameters to be mutipart-stream-encoded.
+ * @param array $requestHeaders Request headers.
+ *
+ * @return ResponseInterface
+ */
+ protected function doMultipart($type, $path, array $parameters = [], array $requestHeaders = [])
+ {
+ Assert::oneOf(
+ $type,
+ [
+ 'DELETE',
+ 'POST',
+ 'PUT',
+ ]
+ );
+
+ $streamFactory = StreamFactoryDiscovery::find();
+ $builder = new MultipartStreamBuilder($streamFactory);
+ foreach ($parameters as $k => $v) {
+ $builder->addResource($k, $v);
+ }
+
+ $multipartStream = $builder->build();
+ $boundary = $builder->getBoundary();
+
+ $request = MessageFactoryDiscovery::find()->createRequest(
+ $type,
+ $path,
+ ['Content-Type' => 'multipart/form-data; boundary='.$boundary],
+ $multipartStream
+ );
+
+ return $this->httpClient->sendRequest($request);
+ }
+
/**
* Create a JSON encoded version of an array of parameters.
*
diff --git a/src/Mailgun/Api/Domain.php b/src/Mailgun/Api/Domain.php
new file mode 100644
index 0000000..d0244fc
--- /dev/null
+++ b/src/Mailgun/Api/Domain.php
@@ -0,0 +1,269 @@
+
+ */
+class Domain extends AbstractApi
+{
+ /**
+ * Returns a list of domains on the account.
+ *
+ * @param int $limit
+ * @param int $skip
+ *
+ * @return DomainListResponse
+ */
+ public function listAll($limit = 100, $skip = 0)
+ {
+ Assert::integer($limit);
+ Assert::integer($skip);
+
+ $params = [
+ 'limit' => $limit,
+ 'skip' => $skip,
+ ];
+
+ $response = $this->get('/v3/domains', $params);
+
+ return $this->serializer->deserialize($response, DomainListResponse::class);
+ }
+
+ /**
+ * Returns a single domain.
+ *
+ * @param string $domain Name of the domain.
+ *
+ * @return ComplexDomain|array|ResponseInterface
+ */
+ public function info($domain)
+ {
+ Assert::stringNotEmpty($domain);
+
+ $response = $this->get(sprintf('/v3/domains/%s', $domain));
+
+ return $this->serializer->deserialize($response, ComplexDomain::class);
+ }
+
+ /**
+ * Creates a new domain for the account.
+ * See below for spam filtering parameter information.
+ * {@link https://documentation.mailgun.com/user_manual.html#um-spam-filter}.
+ *
+ * @param string $domain Name of the domain.
+ * @param string $smtpPass Password for SMTP authentication.
+ * @param string $spamAction `disable` or `tag` - inbound spam filtering.
+ * @param bool $wildcard Domain will accept email for subdomains.
+ *
+ * @return ComplexDomain|array|ResponseInterface
+ */
+ public function create($domain, $smtpPass, $spamAction, $wildcard)
+ {
+ Assert::stringNotEmpty($domain);
+ Assert::stringNotEmpty($smtpPass);
+ // TODO(sean.johnson): Extended spam filter input validation.
+ Assert::stringNotEmpty($spamAction);
+ Assert::boolean($wildcard);
+
+ $params = [
+ 'name' => $domain,
+ 'smtp_password' => $smtpPass,
+ 'spam_action' => $spamAction,
+ 'wildcard' => $wildcard,
+ ];
+
+ $response = $this->postMultipart('/v3/domains', $params);
+
+ return $this->safeDeserialize($response, ComplexDomain::class);
+ }
+
+ /**
+ * Removes a domain from the account.
+ * WARNING: This action is irreversible! Be cautious!
+ *
+ * @param string $domain Name of the domain.
+ *
+ * @return SimpleResponse|array|ResponseInterface
+ */
+ public function remove($domain)
+ {
+ Assert::stringNotEmpty($domain);
+
+ $response = $this->delete(sprintf('/v3/domains/%s', $domain));
+
+ return $this->serializer->deserialize($response, SimpleResponse::class);
+ }
+
+ /**
+ * Returns a list of SMTP credentials for the specified domain.
+ *
+ * @param string $domain Name of the domain.
+ * @param int $limit Number of credentials to return
+ * @param int $skip Number of credentials to omit from the list
+ *
+ * @return CredentialsListResponse
+ */
+ public function listCredentials($domain, $limit = 100, $skip = 0)
+ {
+ Assert::stringNotEmpty($domain);
+ Assert::integer($limit);
+ Assert::integer($skip);
+
+ $params = [
+ 'limit' => $limit,
+ 'skip' => $skip,
+ ];
+
+ $response = $this->get(sprintf('/v3/domains/%s/credentials', $domain), $params);
+
+ return $this->safeDeserialize($response, CredentialListResponse::class);
+ }
+
+ /**
+ * Create a new SMTP credential pair for the specified domain.
+ *
+ * @param string $domain Name of the domain.
+ * @param string $login SMTP Username.
+ * @param string $password SMTP Password. Length min 5, max 32.
+ *
+ * @return Credential|array|ResponseInterface
+ */
+ public function newCredential($domain, $login, $password)
+ {
+ Assert::stringNotEmpty($domain);
+ Assert::stringNotEmpty($login);
+ Assert::stringNotEmpty($password);
+ Assert::lengthBetween($password, 5, 32, 'SMTP password must be between 5 and 32 characters.');
+
+ $params = [
+ 'login' => $login,
+ 'password' => $password,
+ ];
+
+ $response = $this->postMultipart(sprintf('/v3/domains/%s/credentials', $domain), $params);
+
+ return $this->serializer->deserialize($response, SimpleResponse::class);
+ }
+
+ /**
+ * Update a set of SMTP credentials for the specified domain.
+ *
+ * @param string $domain Name of the domain.
+ * @param string $login SMTP Username.
+ * @param string $pass New SMTP Password. Length min 5, max 32.
+ *
+ * @return SimpleResponse|array|ResponseInterface
+ */
+ public function updateCredential($domain, $login, $pass)
+ {
+ Assert::stringNotEmpty($domain);
+ Assert::stringNotEmpty($login);
+ Assert::stringNotEmpty($pass);
+ Assert::lengthBetween($pass, 5, 32, 'SMTP password must be between 5 and 32 characters.');
+
+ $params = [
+ 'password' => $pass,
+ ];
+
+ $response = $this->putMultipart(
+ sprintf(
+ '/v3/domains/%s/credentials/%s',
+ $domain,
+ $login
+ ),
+ $params
+ );
+
+ return $this->serializer->deserialize($response, SimpleResponse::class);
+ }
+
+ /**
+ * Remove a set of SMTP credentials from the specified domain.
+ *
+ * @param string $domain Name of the domain.
+ * @param string $login SMTP Username.
+ *
+ * @return SimpleResponse|array|ResponseInterface
+ */
+ public function deleteCredential($domain, $login)
+ {
+ Assert::stringNotEmpty($domain);
+ Assert::stringNotEmpty($login);
+
+ $response = $this->delete(
+ sprintf(
+ '/v3/domains/%s/credentials/%s',
+ $domain,
+ $login
+ )
+ );
+
+ return $this->serializer->deserialize($response, SimpleResponse::class);
+ }
+
+ /**
+ * Returns delivery connection settings for the specified domain.
+ *
+ * @param string $domain Name of the domain.
+ *
+ * @return DeliverySettingsResponse|array|ResponseInterface
+ */
+ public function getDeliverySettings($domain)
+ {
+ Assert::stringNotEmpty($domain);
+
+ $response = $this->get(sprintf('/v3/domains/%s/connection', $domain));
+
+ return $this->serializer->deserialize($response, DeliverySettingsResponse::class);
+ }
+
+ /**
+ * Updates the specified delivery connection settings for the specified domain.
+ * If a parameter is passed in as null, it will not be updated.
+ *
+ * @param string $domain Name of the domain.
+ * @param bool|null $requireTLS Enforces that messages are sent only over a TLS connection.
+ * @param bool|null $noVerify Disables TLS certificate and hostname verification.
+ *
+ * @return DeliverySettingsResponse|array|ResponseInterface
+ */
+ public function updateDeliverySettings($domain, $requireTLS, $noVerify)
+ {
+ Assert::stringNotEmpty($domain);
+ Assert::nullOrBoolean($requireTLS);
+ Assert::nullOrBoolean($noVerify);
+
+ $params = [];
+
+ if (null !== $requireTLS) {
+ $params['require_tls'] = $requireTLS ? 'true' : 'false';
+ }
+
+ if (null !== $noVerify) {
+ $params['skip_verification'] = $noVerify ? 'true' : 'false';
+ }
+
+ $response = $this->putMultipart(sprintf('/v3/domains/%s/connection', $domain), $params);
+
+ return $this->serializer->deserialize($response, DeliverySettingsUpdateResponse::class);
+ }
+}
diff --git a/src/Mailgun/Mailgun.php b/src/Mailgun/Mailgun.php
index 2d0071a..69482c5 100644
--- a/src/Mailgun/Mailgun.php
+++ b/src/Mailgun/Mailgun.php
@@ -260,4 +260,12 @@ class Mailgun
{
return new Api\Stats($this->httpClient, $this->requestFactory, $this->serializer);
}
+
+ /**
+ * @return Api\Domain
+ */
+ public function getDomainApi()
+ {
+ return new Api\Domain($this->httpClient, $this->requestFactory, $this->serializer);
+ }
}
diff --git a/src/Mailgun/Resource/Api/Domain/ComplexDomain.php b/src/Mailgun/Resource/Api/Domain/ComplexDomain.php
new file mode 100644
index 0000000..cc8cc74
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/ComplexDomain.php
@@ -0,0 +1,95 @@
+
+ */
+class ComplexDomain implements CreatableFromArray
+{
+ /**
+ * @var SimpleDomain
+ */
+ private $domain;
+
+ /**
+ * @var DomainDnsRecord[]
+ */
+ private $inboundDnsRecords;
+
+ /**
+ * @var DomainDnsRecord[]
+ */
+ private $outboundDnsRecords;
+
+ /**
+ * @param array $data
+ *
+ * @return ComplexDomain
+ */
+ public static function createFromArray(array $data)
+ {
+ Assert::keyExists($data, 'domain');
+ Assert::keyExists($data, 'receiving_dns_records');
+ Assert::keyExists($data, 'sending_dns_records');
+
+ // Let DomainDnsRecord::createFromArray() handle validation of
+ // the `receiving_dns_records` and `sending_dns_records` data.
+ // Also let SimpleDomain::createFromArray() handle validation of
+ // the `domain` fields.
+ return new static(
+ SimpleDomain::createFromArray($data['domain']),
+ DomainDnsRecord::createFromArray($data['receiving_dns_records']),
+ DomainDnsRecord::createFromArray($data['sending_dns_records'])
+ );
+ }
+
+ /**
+ * @param SimpleDomain $domainInfo
+ * @param array $rxRecords Array of DomainDnsRecord instances
+ * @param array $txRecords Array of DomainDnsRecord instances
+ */
+ public function __construct(SimpleDomain $domainInfo, array $rxRecords, array $txRecords)
+ {
+ $this->domain = $domainInfo;
+ $this->inboundDnsRecords = $rxRecords;
+ $this->outboundDnsRecords = $txRecords;
+ }
+
+ /**
+ * @return SimpleDomain
+ */
+ public function getDomain()
+ {
+ return $this->domain;
+ }
+
+ /**
+ * @return DomainDnsRecord[]
+ */
+ public function getInboundDNSRecords()
+ {
+ return $this->inboundDnsRecords;
+ }
+
+ /**
+ * @return DomainDnsRecord[]
+ */
+ public function getOutboundDNSRecords()
+ {
+ return $this->outboundDnsRecords;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/Credential.php b/src/Mailgun/Resource/Api/Domain/Credential.php
new file mode 100644
index 0000000..55bbe07
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/Credential.php
@@ -0,0 +1,113 @@
+
+ */
+class Credential implements CreatableFromArray
+{
+ /**
+ * @var int|null
+ */
+ private $sizeBytes;
+
+ /**
+ * @var \DateTime
+ */
+ private $createdAt;
+
+ /**
+ * @var string
+ */
+ private $mailbox;
+
+ /**
+ * @var string
+ */
+ private $login;
+
+ /**
+ * @param array $data
+ *
+ * @return Credential
+ */
+ public static function createFromArray(array $data)
+ {
+ Assert::keyExists($data, 'created_at');
+ Assert::keyExists($data, 'mailbox');
+ Assert::keyExists($data, 'login');
+
+ $sizeBytes = array_key_exists('size_bytes', $data) ? $data['size_bytes'] : null;
+ $createdAt = new \DateTime($data['created_at']);
+ $mailbox = $data['mailbox'];
+ $login = $data['login'];
+
+ Assert::nullOrInteger($sizeBytes);
+ Assert::isInstanceOf($createdAt, '\DateTime');
+ Assert::string($mailbox);
+ Assert::string($login);
+
+ return new static(
+ $sizeBytes,
+ $createdAt,
+ $mailbox,
+ $login
+ );
+ }
+
+ /**
+ * @param int $sizeBytes
+ * @param \DateTime $createdAt
+ * @param string $mailbox
+ * @param string $login
+ */
+ public function __construct($sizeBytes, \DateTime $createdAt, $mailbox, $login)
+ {
+ $this->sizeBytes = $sizeBytes;
+ $this->createdAt = $createdAt;
+ $this->mailbox = $mailbox;
+ $this->login = $login;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getSizeBytes()
+ {
+ return $this->sizeBytes;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreatedAt()
+ {
+ return $this->createdAt;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMailbox()
+ {
+ return $this->mailbox;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLogin()
+ {
+ return $this->login;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/CredentialListResponse.php b/src/Mailgun/Resource/Api/Domain/CredentialListResponse.php
new file mode 100644
index 0000000..e57cd38
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/CredentialListResponse.php
@@ -0,0 +1,78 @@
+
+ */
+class CredentialListResponse implements CreatableFromArray
+{
+ /**
+ * @var int
+ */
+ private $totalCount;
+
+ /**
+ * @var Credential[]
+ */
+ private $items;
+
+ /**
+ * @param array $data
+ *
+ * @return CredentialListResponse|array|ResponseInterface
+ */
+ public static function createFromArray(array $data)
+ {
+ $items = [];
+
+ Assert::keyExists($data, 'total_count');
+ Assert::keyExists($data, 'items');
+
+ foreach ($data['items'] as $item) {
+ $items[] = Credential::createFromArray($item);
+ }
+
+ return new self($data['total_count'], $items);
+ }
+
+ /**
+ * @param int $totalCount
+ * @param Credential[] $items
+ */
+ public function __construct($totalCount, array $items)
+ {
+ Assert::integer($totalCount);
+ Assert::isArray($items);
+ Assert::allIsInstanceOf($items, 'Mailgun\Resource\Api\Domain\Credential');
+
+ $this->totalCount = $totalCount;
+ $this->items = $items;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTotalCount()
+ {
+ return $this->totalCount;
+ }
+
+ /**
+ * @return Credential[]
+ */
+ public function getCredentials()
+ {
+ return $this->items;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/DeliverySettingsResponse.php b/src/Mailgun/Resource/Api/Domain/DeliverySettingsResponse.php
new file mode 100644
index 0000000..9677073
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/DeliverySettingsResponse.php
@@ -0,0 +1,74 @@
+
+ */
+class DeliverySettingsResponse implements CreatableFromArray
+{
+ /**
+ * @var bool
+ */
+ private $noVerify;
+
+ /**
+ * @var bool
+ */
+ private $requireTLS;
+
+ /**
+ * @param array $data
+ *
+ * @return DeliverySettingsResponse
+ */
+ public static function createFromArray(array $data)
+ {
+ Assert::keyExists($data, 'connection');
+ Assert::isArray($data['connection']);
+ $connSettings = $data['connection'];
+
+ Assert::keyExists($connSettings, 'skip_verification');
+ Assert::keyExists($connSettings, 'require_tls');
+
+ return new static(
+ $connSettings['skip_verification'],
+ $connSettings['require_tls']
+ );
+ }
+
+ /**
+ * @param bool $noVerify Disable remote TLS certificate verification
+ * @param bool $requireTLS Requires TLS for all outbound communication
+ */
+ public function __construct($noVerify, $requireTLS)
+ {
+ $this->noVerify = $noVerify;
+ $this->requireTLS = $requireTLS;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getSkipVerification()
+ {
+ return $this->noVerify;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getRequireTLS()
+ {
+ return $this->requireTLS;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/DeliverySettingsUpdateResponse.php b/src/Mailgun/Resource/Api/Domain/DeliverySettingsUpdateResponse.php
new file mode 100644
index 0000000..c6016fc
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/DeliverySettingsUpdateResponse.php
@@ -0,0 +1,91 @@
+
+ */
+class DeliverySettingsUpdateResponse extends SimpleResponse implements CreatableFromArray
+{
+ /**
+ * @var bool
+ */
+ private $noVerify;
+
+ /**
+ * @var bool
+ */
+ private $requireTLS;
+
+ /**
+ * @param array $data
+ *
+ * @return SettingsUpdateResponse
+ */
+ public static function createFromArray(array $data)
+ {
+ Assert::keyExists($data, 'message');
+ Assert::keyExists($data, 'skip_verification');
+ Assert::keyExists($data, 'require_tls');
+
+ $message = $data['message'];
+ $noVerify = $data['skip_verification'];
+ $requireTLS = $data['require_tls'];
+
+ Assert::nullOrString($message);
+ Assert::boolean($noVerify);
+ Assert::boolean($requireTLS);
+
+ return new static(
+ $message,
+ $noVerify,
+ $requireTLS
+ );
+ }
+
+ /**
+ * @param string $message
+ * @param bool $noVerify
+ * @param bool $requireTLS
+ */
+ public function __construct($message, $noVerify, $requireTLS)
+ {
+ $this->message = $message;
+ $this->noVerify = $noVerify;
+ $this->requireTLS = $requireTLS;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getSkipVerification()
+ {
+ return $this->noVerify;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getRequireTLS()
+ {
+ return $this->requireTLS;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/DomainDnsRecord.php b/src/Mailgun/Resource/Api/Domain/DomainDnsRecord.php
new file mode 100644
index 0000000..0a4d1a3
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/DomainDnsRecord.php
@@ -0,0 +1,141 @@
+
+ */
+class DomainDnsRecord implements CreatableFromArray
+{
+ /**
+ * @var string|null
+ */
+ private $name;
+
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @var string
+ */
+ private $value;
+
+ /**
+ * @var string|null
+ */
+ private $priority;
+
+ /**
+ * @var string
+ */
+ private $valid;
+
+ /**
+ * @param array $data
+ *
+ * @return DomainDnsRecord[]|array|ResponseInterface
+ */
+ public static function createFromArray(array $data)
+ {
+ $items = [];
+
+ foreach ($data as $item) {
+ Assert::keyExists($item, 'record_type');
+ Assert::keyExists($item, 'value');
+ Assert::keyExists($item, 'valid');
+
+ $items[] = new static(
+ array_key_exists('name', $item) ? $item['name'] : null,
+ $item['record_type'],
+ $item['value'],
+ array_key_exists('priority', $item) ? $item['priority'] : null,
+ $item['valid']
+ );
+ }
+
+ return $items;
+ }
+
+ /**
+ * @param string|null $name Name of the record, as used in CNAME, etc.
+ * @param string $type DNS record type
+ * @param string $value DNS record value
+ * @param string|null $priority Record priority, used for MX
+ * @param string $valid DNS record has been added to domain DNS?
+ */
+ public function __construct($name, $type, $value, $priority, $valid)
+ {
+ Assert::nullOrString($name);
+ Assert::string($type);
+ Assert::string($value);
+ Assert::nullOrString($priority);
+ Assert::string($valid);
+
+ $this->name = $name;
+ $this->type = $type;
+ $this->value = $value;
+ $this->priority = $priority;
+ $this->valid = $valid;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * @return value
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getPriority()
+ {
+ return $this->priority;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isValid()
+ {
+ return 'valid' === $this->value;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValidity()
+ {
+ return $this->valid;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/DomainListResponse.php b/src/Mailgun/Resource/Api/Domain/DomainListResponse.php
new file mode 100644
index 0000000..fd9a425
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/DomainListResponse.php
@@ -0,0 +1,95 @@
+
+ */
+class DomainListResponse implements CreatableFromArray
+{
+ /**
+ * @var int
+ */
+ private $totalCount;
+
+ /**
+ * @var SimpleDomain[]
+ */
+ private $items;
+
+ /**
+ * @param array $data
+ *
+ * @return DomainListResponse|array|ResponseInterface
+ */
+ public static function createFromArray(array $data)
+ {
+ $items = [];
+
+ Assert::keyExists($data, 'total_count');
+ Assert::keyExists($data, 'items');
+
+ foreach ($data['items'] as $item) {
+ Assert::keyExists($item, 'name');
+ Assert::keyExists($item, 'smtp_login');
+ Assert::keyExists($item, 'smtp_password');
+ Assert::keyExists($item, 'wildcard');
+ Assert::keyExists($item, 'spam_action');
+ Assert::keyExists($item, 'state');
+ Assert::keyExists($item, 'created_at');
+
+ $items[] = SimpleDomain::createFromArray($item);
+ $items[] = new SimpleDomain(
+ $item['name'],
+ $item['smtp_login'],
+ $item['smtp_password'],
+ $item['wildcard'],
+ $item['spam_action'],
+ $item['state'],
+ new \DateTime($item['created_at'])
+ );
+ }
+
+ return new self($data['total_count'], $items);
+ }
+
+ /**
+ * @param int $totalCount
+ * @param SimpleDomain[] $items
+ */
+ public function __construct($totalCount, array $items)
+ {
+ Assert::integer($totalCount);
+ Assert::isArray($items);
+ Assert::allIsInstanceOf($items, 'Mailgun\Resource\Api\Domain\SimpleDomain');
+
+ $this->totalCount = $totalCount;
+ $this->items = $items;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTotalCount()
+ {
+ return $this->totalCount;
+ }
+
+ /**
+ * @return SimpleDomain[]
+ */
+ public function getDomains()
+ {
+ return $this->items;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/Domain/SimpleDomain.php b/src/Mailgun/Resource/Api/Domain/SimpleDomain.php
new file mode 100644
index 0000000..f78f8bc
--- /dev/null
+++ b/src/Mailgun/Resource/Api/Domain/SimpleDomain.php
@@ -0,0 +1,167 @@
+
+ */
+class SimpleDomain implements CreatableFromArray
+{
+ /**
+ * @var \DateTime
+ */
+ private $createdAt;
+
+ /**
+ * @var string
+ */
+ private $smtpLogin;
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var string
+ */
+ private $smtpPassword;
+
+ /**
+ * @var bool
+ */
+ private $wildcard;
+
+ /**
+ * @var string
+ */
+ private $spamAction;
+
+ /**
+ * @var string
+ */
+ private $state;
+
+ /**
+ * @param array $data
+ *
+ * @return SimpleDomain
+ */
+ public static function createFromArray(array $data)
+ {
+ Assert::isArray($data);
+
+ Assert::keyExists($data, 'name');
+ Assert::keyExists($data, 'smtp_login');
+ Assert::keyExists($data, 'smtp_password');
+ Assert::keyExists($data, 'wildcard');
+ Assert::keyExists($data, 'spam_action');
+ Assert::keyExists($data, 'state');
+ Assert::keyExists($data, 'created_at');
+
+ return new static(
+ $data['name'],
+ $data['smtp_login'],
+ $data['smtp_password'],
+ $data['wildcard'],
+ $data['spam_action'],
+ $data['state'],
+ new \DateTime($data['created_at'])
+ );
+ }
+
+ /**
+ * @param string $name
+ * @param string $smtpLogin
+ * @param string $smtpPass
+ * @param bool $wildcard
+ * @param string $spamAction
+ * @param string $state
+ * @param \DateTime $createdAt
+ */
+ public function __construct($name, $smtpLogin, $smtpPassword, $wildcard, $spamAction, $state, \DateTime $createdAt)
+ {
+ Assert::string($name);
+ Assert::string($smtpLogin);
+ Assert::string($smtpPassword);
+ Assert::boolean($wildcard);
+ Assert::string($spamAction);
+ Assert::string($state);
+ Assert::isInstanceOf($createdAt, '\DateTime');
+
+ $this->name = $name;
+ $this->smtpLogin = $smtpLogin;
+ $this->smtpPassword = $smtpPassword;
+ $this->wildcard = $wildcard;
+ $this->spamAction = $spamAction;
+ $this->state = $state;
+ $this->createdAt = $createdAt;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSmtpUsername()
+ {
+ return $this->smtpLogin;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSmtpPassword()
+ {
+ return $this->smtpPassword;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isWildcard()
+ {
+ return $this->wildcard;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSpamAction()
+ {
+ return $this->spamAction;
+ }
+
+ /**
+ * @return string
+ */
+ public function getState()
+ {
+ return $this->state;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreatedAt()
+ {
+ return $this->createdAt;
+ }
+}
diff --git a/src/Mailgun/Resource/Api/SimpleResponse.php b/src/Mailgun/Resource/Api/SimpleResponse.php
new file mode 100644
index 0000000..bb11999
--- /dev/null
+++ b/src/Mailgun/Resource/Api/SimpleResponse.php
@@ -0,0 +1,99 @@
+
+ */
+class SimpleResponse implements CreatableFromArray
+{
+ /**
+ * @var string
+ */
+ private $message;
+
+ /**
+ * Only set when API rate limit is hit and a rate limit response is returned.
+ *
+ * @var int
+ */
+ private $retrySeconds = null;
+
+ /**
+ * Only set on calls such as DELETE /v3/domains/.../credentials/.
+ *
+ * @var string
+ */
+ private $spec = null;
+
+ /**
+ * @param array $data
+ *
+ * @return SimpleResponse
+ */
+ public static function createFromArray(array $data)
+ {
+ $message = array_key_exists('message', $data) ? $data['message'] : null;
+ $retrySeconds = array_key_exists('retry_seconds', $data) ? $data['retry_seconds'] : null;
+ $spec = array_key_exists('spec', $data) ? $data['spec'] : null;
+
+ return new static($message, $retrySeconds, $spec);
+ }
+
+ /**
+ * @param string|null $message
+ * @param int|null $retrySeconds
+ * @param string|null $spec
+ */
+ public function __construct($message, $retrySeconds, $spec)
+ {
+ Assert::nullOrString($message);
+ Assert::nullOrInteger($retrySeconds);
+ Assert::nullOrString($spec);
+
+ $this->message = $message;
+ $this->retrySeconds = $retrySeconds;
+ $this->spec = $spec;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSpec()
+ {
+ return $this->spec;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRateLimited()
+ {
+ return null !== $this->retrySeconds;
+ }
+
+ /**
+ * @return int
+ */
+ public function getRetrySeconds()
+ {
+ return $this->retrySeconds;
+ }
+}
diff --git a/tests/Api/StatsTest.php b/tests/Api/StatsTest.php
index 656519c..b17eecc 100644
--- a/tests/Api/StatsTest.php
+++ b/tests/Api/StatsTest.php
@@ -29,14 +29,15 @@ class StatsTest extends TestCase
$api->total('domain', $data);
}
- /**
- * expectedException \Mailgun\Exception\InvalidArgumentException.
- */
- //public function testTotalInvalidArgument()
- //{
- // $api = $this->getApiMock();
- // $api->total('');
- //}
+ // /**
+ // * @expectedException \Mailgun\Exception\InvalidArgumentException
+ // */
+ // public function testTotalInvalidArgument()
+ // {
+ // $api = $this->getApiMock();
+ //
+ // $api->total('');
+ // }
public function testAll()
{
@@ -53,12 +54,13 @@ class StatsTest extends TestCase
$api->all('domain', $data);
}
- /*
- * expectedException \Mailgun\Exception\InvalidArgumentException
- */
- //public function testAllInvalidArgument()
- //{
- // $api = $this->getApiMock();
- // $api->all('');
- //}
+ // /**
+ // * @expectedException \Mailgun\Exception\InvalidArgumentException
+ // */
+ // public function testAllInvalidArgument()
+ // {
+ // $api = $this->getApiMock();
+ //
+ // $api->all('');
+ // }
}
diff --git a/tests/Api/TestCase.php b/tests/Api/TestCase.php
index 0ac2e93..33eaea1 100644
--- a/tests/Api/TestCase.php
+++ b/tests/Api/TestCase.php
@@ -8,6 +8,34 @@ namespace Mailgun\Tests\Api;
*/
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
+ /**
+ * Private Mailgun API key.
+ *
+ * @var string
+ */
+ protected $apiPrivKey;
+
+ /**
+ * Public Mailgun API key.
+ *
+ * @var string
+ */
+ protected $apiPubKey;
+
+ /**
+ * Domain used for API testing.
+ *
+ * @var string
+ */
+ protected $testDomain;
+
+ public function __construct()
+ {
+ $this->apiPrivKey = getenv('MAILGUN_PRIV_KEY');
+ $this->apiPubKey = getenv('MAILGUN_PUB_KEY');
+ $this->testDomain = getenv('MAILGUN_DOMAIN');
+ }
+
abstract protected function getApiClass();
protected function getApiMock()
@@ -28,8 +56,20 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
->getMock();
return $this->getMockBuilder($this->getApiClass())
- ->setMethods(['get', 'post', 'postRaw', 'delete', 'put'])
+ ->setMethods(
+ [
+ 'get',
+ 'post', 'postRaw', 'postMultipart',
+ 'delete', 'deleteMultipart',
+ 'put', 'putMultipart',
+ ]
+ )
->setConstructorArgs([$httpClient, $requestClient, $serializer])
->getMock();
}
+
+ protected function getMailgunClient()
+ {
+ return new \Mailgun\Mailgun($this->apiPrivKey);
+ }
}
diff --git a/tests/Integration/DomainApiTest.php b/tests/Integration/DomainApiTest.php
new file mode 100644
index 0000000..239e7f6
--- /dev/null
+++ b/tests/Integration/DomainApiTest.php
@@ -0,0 +1,352 @@
+
+ */
+class DomainApiTest extends TestCase
+{
+ protected function getApiClass()
+ {
+ return 'Mailgun\Api\Domains';
+ }
+
+ /**
+ * Performs `GET /v3/domains` and ensures $this->testDomain exists
+ * in the returned list.
+ */
+ public function testDomainsList()
+ {
+ $mg = $this->getMailgunClient();
+
+ $domainList = $mg->getDomainApi()->listAll();
+ $found = false;
+ foreach ($domainList->getDomains() as $domain) {
+ if ($domain->getName() === $this->testDomain) {
+ $found = true;
+ }
+ }
+
+ $this->assertContainsOnlyInstancesOf(SimpleDomain::class, $domainList->getDomains());
+ $this->assertTrue($found);
+ }
+
+ /**
+ * Performs `GET /v3/domains/` and ensures $this->testDomain
+ * is properly returned.
+ */
+ public function testDomainGet()
+ {
+ $mg = $this->getMailgunClient();
+
+ $domain = $mg->getDomainApi()->info($this->testDomain);
+ $this->assertNotNull($domain);
+ $this->assertNotNull($domain->getDomain());
+ $this->assertNotNull($domain->getInboundDNSRecords());
+ $this->assertNotNull($domain->getOutboundDNSRecords());
+ $this->assertEquals($domain->getDomain()->getState(), 'active');
+ }
+
+ /**
+ * Performs `DELETE /v3/domains/` on a non-existent domain.
+ */
+ public function testRemoveDomain_NoExist()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->remove('example.notareal.tld');
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Domain not found', $ret->getMessage());
+ }
+
+ /**
+ * Performs `POST /v3/domains` to attempt to create a domain with valid
+ * values.
+ */
+ public function testDomainCreate()
+ {
+ $mg = $this->getMailgunClient();
+
+ $domain = $mg->getDomainApi()->create(
+ 'example.notareal.tld', // domain name
+ 'exampleOrgSmtpPassword12', // smtp password
+ 'tag', // default spam action
+ false // wildcard domain?
+ );
+ $this->assertNotNull($domain);
+ $this->assertNotNull($domain->getDomain());
+ $this->assertNotNull($domain->getInboundDNSRecords());
+ $this->assertNotNull($domain->getOutboundDNSRecords());
+ }
+
+ /**
+ * Performs `POST /v3/domains` to attempt to create a domain with duplicate
+ * values.
+ */
+ public function testDomainCreate_DuplicateValues()
+ {
+ $mg = $this->getMailgunClient();
+
+ $domain = $mg->getDomainApi()->create(
+ 'example.notareal.tld', // domain name
+ 'exampleOrgSmtpPassword12', // smtp password
+ 'tag', // default spam action
+ false // wildcard domain?
+ );
+ $this->assertNotNull($domain);
+ $this->assertInstanceOf(SimpleResponse::class, $domain);
+ $this->assertEquals('This domain name is already taken', $domain->getMessage());
+ }
+
+ /**
+ * Performs `DELETE /v3/domains/` to remove a domain from the account.
+ */
+ public function testRemoveDomain()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->remove('example.notareal.tld');
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Domain has been deleted', $ret->getMessage());
+ }
+
+ /**
+ * Performs `POST /v3/domains//credentials` to add a credential pair
+ * to the domain.
+ */
+ public function testCreateCredential()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->newCredential(
+ $this->testDomain,
+ 'user-test@'.$this->testDomain,
+ 'Password.01!'
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Created 1 credentials pair(s)', $ret->getMessage());
+ }
+
+ /**
+ * Performs `POST /v3/domains//credentials` to attempt to add an invalid
+ * credential pair.
+ *
+ * @expectedException InvalidArgumentException
+ */
+ public function testCreateCredentialBadPasswordLong()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->newCredential(
+ $this->testDomain,
+ 'user-test',
+ 'ExtremelyLongPasswordThatCertainlyWillNotBeAccepted'
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ }
+
+ /**
+ * Performs `POST /v3/domains//credentials` to attempt to add an invalid
+ * credential pair.
+ *
+ * @expectedException InvalidArgumentException
+ */
+ public function testCreateCredentialBadPasswordShort()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->newCredential(
+ $this->testDomain,
+ 'user-test',
+ 'no'
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ }
+
+ /**
+ * Performs `GET /v3/domains//credentials` to get a list of active credentials.
+ */
+ public function testListCredentials()
+ {
+ $mg = $this->getMailgunClient();
+
+ $found = false;
+
+ $ret = $mg->getDomainApi()->listCredentials($this->testDomain);
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(CredentialListResponse::class, $ret);
+ $this->assertContainsOnlyInstancesOf(Credential::class, $ret->getCredentials());
+
+ foreach ($ret->getCredentials() as $cred) {
+ if ($cred->getLogin() === 'user-test@'.$this->testDomain) {
+ $found = true;
+ }
+ }
+
+ $this->assertTrue($found);
+ }
+
+ /**
+ * Performs `GET /v3/domains//credentials` on a non-existent domain.
+ */
+ public function testListCredentialsBadDomain()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->listCredentials('mailgun.org');
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Domain not found: mailgun.org', $ret->getMessage());
+ }
+
+ /**
+ * Performs `PUT /v3/domains//credentials/` to update a credential's
+ * password.
+ */
+ public function testUpdateCredential()
+ {
+ $login = 'user-test@'.$this->testDomain;
+
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->updateCredential(
+ $this->testDomain,
+ $login,
+ 'Password..02!'
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Password changed', $ret->getMessage());
+ }
+
+ /**
+ * Performs `PUT /v3/domains//credentials/` with a bad password.
+ *
+ * @expectedException InvalidArgumentException
+ */
+ public function testUpdateCredentialBadPasswordLong()
+ {
+ $login = 'user-test@'.$this->testDomain;
+
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->updateCredential(
+ $this->testDomain,
+ $login,
+ 'ThisIsAnExtremelyLongPasswordThatSurelyWontBeAccepted'
+ );
+ $this->assertNotNull($ret);
+ }
+
+ /**
+ * Performs `PUT /v3/domains//credentials/` with a bad password.
+ *
+ * @expectedException InvalidArgumentException
+ */
+ public function testUpdateCredentialBadPasswordShort()
+ {
+ $login = 'user-test@'.$this->testDomain;
+
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->updateCredential(
+ $this->testDomain,
+ $login,
+ 'no'
+ );
+ $this->assertNotNull($ret);
+ }
+
+ /**
+ * Performs `DELETE /v3/domains//credentials/` to remove a credential
+ * pair from a domain.
+ */
+ public function testRemoveCredential()
+ {
+ $login = 'user-test@'.$this->testDomain;
+
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->deleteCredential(
+ $this->testDomain,
+ $login
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Credentials have been deleted', $ret->getMessage());
+ $this->assertEquals($login, $ret->getSpec());
+ }
+
+ /**
+ * Performs `DELETE /v3/domains//credentials/` to remove an invalid
+ * credential pair from a domain.
+ */
+ public function testRemoveCredentialNoExist()
+ {
+ $login = 'user-noexist-test@'.$this->testDomain;
+
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->deleteCredential(
+ $this->testDomain,
+ $login
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(SimpleResponse::class, $ret);
+ $this->assertEquals('Credentials not found', $ret->getMessage());
+ }
+
+ /**
+ * Performs `GET /v3/domains//connection` to retrieve connection settings.
+ */
+ public function testGetDeliverySettings()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->getDeliverySettings($this->testDomain);
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(DeliverySettingsResponse::class, $ret);
+ $this->assertTrue(is_bool($ret->getSkipVerification()));
+ $this->assertTrue(is_bool($ret->getRequireTLS()));
+ }
+
+ /**
+ * Performs `PUT /v3/domains//connection` to set connection settings.
+ */
+ public function testSetDeliverySettings()
+ {
+ $mg = $this->getMailgunClient();
+
+ $ret = $mg->getDomainApi()->updateDeliverySettings(
+ $this->testDomain,
+ true,
+ false
+ );
+ $this->assertNotNull($ret);
+ $this->assertInstanceOf(DeliverySettingsUpdateResponse::class, $ret);
+ $this->assertEquals('Domain connection settings have been updated, may take 10 minutes to fully propagate', $ret->getMessage());
+ $this->assertEquals(true, $ret->getRequireTLS());
+ $this->assertEquals(false, $ret->getSkipVerification());
+ }
+}