File: //usr/local/mailchannels/clients/InboundApi/InboundApiImpl.php
<?php
namespace MailChannels;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use Psr\Http\Message\ResponseInterface;
class InboundApiImpl implements InboundApi {
const MAXIMUM_DOWNSTREAM_ADDRESSES = 10;
const MAXIMUM_TARGET_LENGTH = 255;
private $client;
private $logger;
public function __construct($baseUri, $apiKey, Client $client=null) {
if(!$client) {
$client = new Client([
'base_uri'=>$baseUri,
'headers'=>[
'X-Api-Key'=>$apiKey,
'Accept'=>'application/json'
],
'verify' => APP::verifySSL()
]);
}
$this->client = $client;
$this->logger = \MailChannels\App::getLogger();
}
/**
* @param $domain
* @param $subscriptionHandle
* @return Domain|WHM\Domain
* @throws InboundApiConflictException
* @throws InboundApiException
* @throws InboundApiForbiddenException
* @throws InboundApiInternalServerErrorException
* @throws InboundApiUnauthorizedException
*/
public function provisionDomain($domain, $subscriptionHandle) {
if(!is_string($domain) || !is_string($subscriptionHandle)) {
throw new InboundApiException("arguments are not of string");
}
$uri = "domains";
$requestBody = [
'domain' => $domain,
'subscriptionHandle' => $subscriptionHandle
];
try {
$response = $this->call('POST', $uri, [
'headers' => [
'Content-Type' => 'application/json'
],
'body' => json_encode($requestBody)
]);
$jsonResponse = json_decode($response->getBody()->getContents());
if(!in_array((int) $response->getStatusCode(), [200, 201])) {
throw new InboundApiInternalServerErrorException("status code is " . $response->getStatusCode());
}
return (new DomainBuilder())->setDomain($jsonResponse->domain)
->setSubscriptionHandle($jsonResponse->subscriptionHandle)->build();
} catch (ClientException $e) {
//Guzzle throws client exception on 400 error codes
$message = $this->getErrorMessageFromResponse($e->getResponse());
switch($e->getCode()) {
case 401:
throw new InboundApiUnauthorizedException($message);
case 403:
throw new InboundApiForbiddenException($message);
case 409:
throw new InboundApiConflictException($message);
default:
throw new InboundApiInternalServerErrorException($message);
}
}
}
/**
* @param $domain
* @throws InboundApiException
* @throws InboundApiForbiddenException
* @throws InboundApiInternalServerErrorException
* @throws InboundApiNotFoundException
* @throws InboundApiUnauthorizedException
*/
public function deprovisionDomain($domain) {
if(!is_string($domain)) {
throw new InboundApiException("arguments are not of string");
}
$uri = "domains";
try {
$response = $this->call('DELETE', "$uri/$domain");
if($response->getStatusCode() != 204) {
throw new InboundApiInternalServerErrorException($this->getErrorMessageFromResponse($response));
}
return;
} catch (ClientException $e) {
$message = $this->getErrorMessageFromResponse($e->getResponse());
switch ($e->getCode()) {
case 401:
throw new InboundApiUnauthorizedException($message);
case 403:
throw new InboundApiForbiddenException($message);
case 404:
throw new InboundApiNotFoundException($message);
case 422:
throw new InboundApiUnprocessableException($message);
default:
throw new InboundApiInternalServerErrorException($message);
}
} catch (\Exception $e) {
throw new InboundApiException($e->getMessage());
}
}
/**
* @return array
* @throws InboundApiConnectException
* @throws InboundApiInternalServerErrorException
* @throws InboundApiNotFoundException
* @throws InboundApiUnauthorizedException
*/
public function getSubscriptions() {
$uri = "subscriptions";
try {
$response = $this->call('GET', $uri);
$jsonResponse = json_decode($response->getBody()->getContents());
$resultSubscriptions = array();
foreach($jsonResponse as $subscription) {
$resultLimits = array();
foreach($subscription->limits as $limit) {
$builder = new LimitBuilder();
$builder
->setValue($limit->value)
->setFeatureHandle($limit->featureHandle);
$resultLimits[] = $builder->build();
}
$subscriptionBuilder = (new SubscriptionBuilder())
->setHandle($subscription->handle)
->setActive($subscription->active)
->setLimits($resultLimits);
if (isset($subscription->activeAccountsCount)) {
$subscriptionBuilder->setActiveAccountsCount($subscription->activeAccountsCount);
}
if (isset($subscription->plan)) {
$subscriptionBuilder->setPlan(
(new PlanBuilder())
->deserializeJson($subscription->plan)
->build()
);
}
$resultSubscriptions[] = $subscriptionBuilder->build();
}
return $resultSubscriptions;
} catch (ClientException $e) {
$message = $this->getErrorMessageFromResponse($e->getResponse());
switch ($e->getCode()) {
case 401:
throw new InboundApiUnauthorizedException($message);
case 404:
throw new InboundApiNotFoundException($message);
default:
throw new InboundApiInternalServerErrorException($message);
}
}
}
public function getDomains($domainNames=null) {
if ($domainNames && !is_array($domainNames)) {
$domainNames = [$domainNames];
}
$domains = array();
$total = 0;
$params=null;
if (!empty($domainNames)) {
$params = http_build_query([
'domains' => implode(',', $domainNames)
]);
}
try {
$response = $this->call('GET', "domains?$params");
$json = json_decode($response->getBody()->getContents());
$total = $json->total;
foreach ($json->domains as $domain) {
$domains[$domain->domain] = (new DomainBuilder())
->setDomain($domain->domain)
->setSubscriptionHandle($domain->subscriptionHandle)
->build();
}
} catch (ClientException $e) {
$message = $this->getErrorMessageFromResponse($e->getResponse());
switch ($e->getCode()) {
case 401:
throw new InboundApiUnauthorizedException($message, $e->getCode(), $e);
default:
throw new InboundApiInternalServerErrorException($message, $e->getCode(), $e);
}
}
return new GetDomainsResponse($domains, $total);
}
/**
* @param $method
* @param $uri
* @param $options
* @return ResponseInterface
* @throws InboundApiConnectException
*/
protected function call($method, $uri, $options=[]) {
try {
App::getLogger()->debug("[MailChannels Inbound API] request - method: $method, URI: $uri; options: " . json_encode($options));
return $this->client->request($method, $uri, $options);
} catch (ClientException $e) {
App::getLogger()->debug("[MailChannels Inbound API] exception - method: $method URI: $uri; options: " . json_encode($options));
App::getLogger()->debug($e);
throw $e;
} catch (ConnectException $e) {
throw new InboundApiConnectException("Couldn't connect to the mailchannels api", $e->getCode(), $e);
}
}
private function getErrorMessageFromResponse($response) {
$jsonResponse = json_decode($response->getBody()->getContents());
if($response == null || $jsonResponse == null) {
return "";
}
if(property_exists($jsonResponse, "message")) {
return $jsonResponse->message;
}
return "";
}
public function getDownstreamAddresses($domain, $limit=null, $offset=null) {
$downstreamAddressRecords = array();
try {
$response = $this->call('GET', "domains/$domain/downstream-address");
$json = json_decode($response->getBody()->getContents());
App::getLogger()->info("response from the API " . json_encode($json));
foreach ($json->records as $record) {
$downstreamAddressRecords[] = $downstreamAddressRecord = (new DownstreamAddressBuilder())
->setTarget($record->target)
->setPriority($record->priority)
->setPort($record->port)
->setWeight($record->weight)
->build();
}
} catch (ClientException $e) {
$message = $this->getErrorMessageFromResponse($e->getResponse());
switch($e->getCode()) {
case 403:
throw new InboundApiUnauthorizedException($message);
case 404:
throw new InboundApiNotFoundException($message);
default:
throw new InboundApiInternalServerErrorException($message);
}
}
return $downstreamAddressRecords;
}
public function setDownstreamAddresses($domain, $records)
{
if(sizeof($records)>self::MAXIMUM_DOWNSTREAM_ADDRESSES){
throw new InboundApiException("too many downstream addresses");
}
foreach($records as $record){
if(strlen($record->getTarget())>self::MAXIMUM_TARGET_LENGTH){
throw new InboundApiException("target longer than maximum length");
}
}
$downstreamAddressRecords = array();
foreach ($records as $record) {
$downstreamAddressRecords[] = $downstreamAddressRecord = $record->jsonSerialize();
}
$requestBody = [
"records" => $downstreamAddressRecords
];
try {
$url = "domains/$domain/downstream-address";
$response = $this->call('PUT', $url, [
'headers' => [
'Content-Type' => 'application/json'
],
'body' => json_encode($requestBody)
]);
if ($response->getStatusCode() != 201) {
throw new InboundApiInternalServerErrorException($this->getErrorMessageFromResponse($response));
}
return null;
} catch (ClientException $e) {
$message = $this->getErrorMessageFromResponse($e->getResponse());
switch($e->getCode()) {
case 403:
throw new InboundApiUnauthorizedException($message);
case 404:
throw new InboundApiConflictException($message);
default:
throw new InboundApiInternalServerErrorException($message);
}
}
}
}