summaryrefslogtreecommitdiffstats
path: root/lib/SparkPost
diff options
context:
space:
mode:
authorVincent Song <vincent.wsong@gmail.com>2016-06-10 15:37:01 -0400
committerVincent Song <vincent.wsong@gmail.com>2016-06-10 15:37:01 -0400
commitec87c5aec0de52271268109f86a1e3aad7fec643 (patch)
tree6b13598b9658ec49d09f2a30a3728a193ff3443f /lib/SparkPost
parent5dd18d3d8141c038ec454b7ae40d53f0d5b94123 (diff)
parent2b42579b80783dee90197579d9a15208cb87a71e (diff)
downloadphp-sparkpost-ec87c5aec0de52271268109f86a1e3aad7fec643.zip
php-sparkpost-ec87c5aec0de52271268109f86a1e3aad7fec643.tar.gz
php-sparkpost-ec87c5aec0de52271268109f86a1e3aad7fec643.tar.bz2
WIP
Diffstat (limited to 'lib/SparkPost')
-rw-r--r--lib/SparkPost/APIResource.php265
-rw-r--r--lib/SparkPost/APIResponseException.php62
-rw-r--r--lib/SparkPost/MessageEvents.php64
-rw-r--r--lib/SparkPost/SparkPost.php208
-rw-r--r--lib/SparkPost/SparkPostException.php26
-rw-r--r--lib/SparkPost/SparkPostPromise.php43
-rw-r--r--lib/SparkPost/SparkPostResponse.php104
7 files changed, 273 insertions, 499 deletions
diff --git a/lib/SparkPost/APIResource.php b/lib/SparkPost/APIResource.php
deleted file mode 100644
index 36b03a8..0000000
--- a/lib/SparkPost/APIResource.php
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php
-
-namespace SparkPost;
-
-/**
- * SDK interface for managing SparkPost API endpoints.
- */
-class APIResource
-{
- /**
- * name of the API endpoint, mainly used for URL construction.
- * This is public to provide an interface.
- *
- * @var string
- */
- public $endpoint;
-
- /**
- * Mapping for values passed into the send method to the values needed for the respective API.
- *
- * @var array
- */
- protected static $parameterMappings = [];
-
- /**
- * Sets up default structure and default values for the model that is acceptable by the API.
- *
- * @var array
- */
- protected static $structure = [];
-
- /**
- * SparkPost reference for httpAdapters and configs.
- */
- protected $sparkpost;
-
- /**
- * Initializes config and httpAdapter for use later.
- *
- * @param $sparkpost \SparkPost\SparkPost provides api configuration information
- */
- public function __construct(SparkPost $sparkpost)
- {
- $this->sparkpost = $sparkpost;
- }
-
- /**
- * Private Method helper to reference parameter mappings and set the right value for the right parameter.
- *
- * @param array $model (pass by reference) the set of values to map
- * @param string $mapKey a dot syntax path determining which value to set
- * @param mixed $value value for the given path
- */
- protected function setMappedValue(&$model, $mapKey, $value)
- {
- //get mapping
- if (empty(static::$parameterMappings)) {
- // if parameterMappings is empty we can assume that no wrapper is defined
- // for the current endpoint and we will use the mapKey to define the mappings directly
- $mapPath = $mapKey;
- } elseif (array_key_exists($mapKey, static::$parameterMappings)) {
- // use only defined parameter mappings to construct $model
- $mapPath = static::$parameterMappings[$mapKey];
- } else {
- return;
- }
-
- $path = explode('.', $mapPath);
- $temp = &$model;
- foreach ($path as $key) {
- if (!isset($temp[$key])) {
- $temp[$key] = null;
- }
- $temp = &$temp[$key];
- }
- $temp = $value;
- }
-
- /**
- * maps values from the passed in model to those needed for the request.
- *
- * @param array $requestConfig the passed in model
- * @param array $model the set of defaults
- *
- * @return array A model ready for the body of a request
- */
- protected function buildRequestModel(array $requestConfig, array $model = [])
- {
- foreach ($requestConfig as $key => $value) {
- $this->setMappedValue($model, $key, $value);
- }
-
- return $model;
- }
-
- /**
- * posts to the api with a supplied body.
- *
- * @param array $body post body for the request
- *
- * @return array Result of the request
- */
- public function create(array $body = [])
- {
- return $this->callResource('post', null, ['body' => $body]);
- }
-
- /**
- * Makes a put request to the api with a supplied body.
- *
- * @param $resourcePath
- * @param array $body Put body for the request
- *
- * @return array Result of the request
- *
- * @throws APIResponseException
- */
- public function update($resourcePath, array $body = [])
- {
- return $this->callResource('put', $resourcePath, ['body' => $body]);
- }
-
- /**
- * Wrapper method for issuing GET request to current API endpoint.
- *
- * @param string $resourcePath (optional) string resource path of specific resource
- * @param array $query (optional) query string parameters
- *
- * @return array Result of the request
- */
- public function get($resourcePath = null, array $query = [])
- {
- return $this->callResource('get', $resourcePath, ['query' => $query]);
- }
-
- /**
- * Wrapper method for issuing DELETE request to current API endpoint.
- *
- * @param string $resourcePath (optional) string resource path of specific resource
- * @param array $query (optional) query string parameters
- *
- * @return array Result of the request
- */
- public function delete($resourcePath = null, array $query = [])
- {
- return $this->callResource('delete', $resourcePath, ['query' => $query]);
- }
-
- /**
- * assembles a URL for a request.
- *
- * @param string $resourcePath path after the initial endpoint
- * @param array $options array with an optional value of query with values to build a querystring from. Any
- * query elements that are themselves arrays will be imploded into a comma separated list.
- *
- * @return string the assembled URL
- */
- private function buildUrl($resourcePath, $options)
- {
- $url = "/{$this->endpoint}/";
- if (!is_null($resourcePath)) {
- $url .= $resourcePath;
- }
-
- if (!empty($options['query'])) {
- // check each query element - if it's an array, implode it to match the API-accepted format
- foreach ($options['query'] as &$element) {
- if (is_array($element)) {
- $element = implode(',', $element);
- }
- }
-
- $queryString = http_build_query($options['query']);
- $url .= '?'.$queryString;
- }
-
- return $url;
- }
-
- /**
- * Prepares a body for put and post requests.
- *
- * @param array $options array with an optional value of body with values to build a request body from.
- *
- * @return string|null A json encoded string or null if no body was provided
- */
- private function buildBody($options)
- {
- $body = null;
- if (!empty($options['body'])) {
- $model = static::$structure;
- $requestModel = $this->buildRequestModel($options['body'], $model);
- $body = json_encode($requestModel);
- }
-
- return $body;
- }
-
- /**
- * Private Method for issuing GET and DELETE request to current API endpoint.
- *
- * This method is responsible for getting the collection _and_
- * a specific entity from the API endpoint
- *
- * If resourcePath parameter is omitted, then we fetch the collection
- *
- * @param string $action HTTP method type
- * @param string $resourcePath (optional) string resource path of specific resource
- * @param array $options (optional) query string parameters
- *
- * @return array Result set of action performed on resource
- *
- * @throws APIResponseException
- */
- private function callResource($action, $resourcePath = null, $options = [])
- {
- $action = strtoupper($action); // normalize
-
- $url = $this->buildUrl($resourcePath, $options);
- $body = $this->buildBody($options);
-
- //make request
- try {
- $response = $this->sparkpost->httpAdapter->send($url, $action, $this->sparkpost->getHttpHeaders(), $body);
-
- $statusCode = $response->getStatusCode();
-
- // Handle 4XX responses, 5XX responses will throw an HttpAdapterException
- if ($statusCode < 400) {
- return json_decode($response->getBody()->getContents(), true);
- } elseif ($statusCode === 403) {
- $response = json_decode($response->getBody(), true);
- throw new APIResponseException(
- 'Request forbidden',
- $statusCode,
- isset($response['errors'][0]['message']) ? $response['errors'][0]['message'] : 'Request forbidden',
- isset($response['errors'][0]['code']) ? $response['errors'][0]['code'] : 1100,
- isset($response['errors'][0]['description']) ? $response['errors'][0]['description'] : 'Does this API Key have the correct permissions?'
- );
- } elseif ($statusCode === 404) {
- throw new APIResponseException('The specified resource does not exist', 404);
- } else {
- $response = json_decode($response->getBody(), true);
- throw new APIResponseException(
- 'Received bad response from '.ucfirst($this->endpoint),
- $statusCode,
- isset($response['errors'][0]['message']) ? $response['errors'][0]['message'] : '',
- isset($response['errors'][0]['code']) ? $response['errors'][0]['code'] : 0,
- isset($response['errors'][0]['description']) ? $response['errors'][0]['description'] : ''
- );
- }
- }
-
- /*
- * Configuration Errors, and a catch all for other errors
- */
- catch (\Exception $exception) {
- if ($exception instanceof APIResponseException) {
- throw $exception;
- }
-
- throw new APIResponseException('Unable to contact '.ucfirst($this->endpoint).' API: '.$exception->getMessage(), $exception->getCode());
- }
- }
-}
diff --git a/lib/SparkPost/APIResponseException.php b/lib/SparkPost/APIResponseException.php
deleted file mode 100644
index a491b3c..0000000
--- a/lib/SparkPost/APIResponseException.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace SparkPost;
-
-class APIResponseException extends \Exception
-{
- /**
- * @var string
- */
- protected $apiMessage;
-
- /**
- * @var int
- */
- protected $apiCode;
-
- /**
- * @var string
- */
- protected $apiDescription;
-
- /**
- * Construct the exception.
- */
- public function __construct($message = '', $code = 0, $apiMessage = '', $apiCode = 0, $apiDescription = '')
- {
- $this->apiMessage = $apiMessage;
- $this->apiCode = $apiCode;
- $this->apiDescription = $apiDescription;
- parent::__construct($message, $code);
- }
-
- /**
- * Gets the Exception message.
- *
- * @return string the Exception message as a string.
- */
- public function getAPIMessage()
- {
- return $this->apiMessage;
- }
-
- /**
- * Gets the API Exception code.
- *
- * @return int the exception code as integer.
- */
- public function getAPICode()
- {
- return $this->apiCode;
- }
-
- /**
- * Gets the Exception description.
- *
- * @return string the Exception description as a string.
- */
- public function getAPIDescription()
- {
- return $this->apiDescription;
- }
-}
diff --git a/lib/SparkPost/MessageEvents.php b/lib/SparkPost/MessageEvents.php
deleted file mode 100644
index d44a30f..0000000
--- a/lib/SparkPost/MessageEvents.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-namespace SparkPost;
-
-/**
- * SDK class for querying the Message Events API.
- *
- * @see https://developers.sparkpost.com/api/#/reference/message-events
- */
-class MessageEvents extends APIResource
-{
- /**
- * @var string
- */
- public $endpoint = 'message-events';
-
- /**
- * Method for issuing search requests to the Message Events API.
- *
- * The method passes-through all of the query parameters - the valid ones are listed at
- *
- * @link https://developers.sparkpost.com/api/#/reference/message-events/events-documentation/search-for-message-events
- *
- * @param array $queryParams The query parameters. Note that a query parameter containing an array
- * is collapsed into a comma-separated list.
- *
- * @return array The result of the query.
- */
- public function search(array $queryParams)
- {
- // check for DateTime objects & replace them with the formatted string equivalent
- foreach (['from', 'to'] as $dateTimeParam) {
- if (isset($queryParams[$dateTimeParam]) && $queryParams[$dateTimeParam] instanceof \DateTime) {
- // the message events API doesn't allow the seconds or GMT offset, so strip them
- $queryParams[$dateTimeParam] = substr($queryParams[$dateTimeParam]->format(\DateTime::ATOM), 0, 16);
- }
- }
-
- return $this->get(null, $queryParams);
- }
-
- /**
- * List descriptions of the event fields that could be included in a response from the MessageEvent::search() method.
- *
- * @return array The event field descriptions.
- */
- public function documentation()
- {
- return $this->get('events/documentation');
- }
-
- /**
- * List examples of the event data that will be included in a response from the MessageEvent::search() method.
- *
- * @param array $events (optional) Event types for which to get a sample payload. If not provided, samples
- * for all events will be returned.
- *
- * @return array Sample events.
- */
- public function samples(array $events = [])
- {
- return $this->get('events/samples', ['events' => $events]);
- }
-}
diff --git a/lib/SparkPost/SparkPost.php b/lib/SparkPost/SparkPost.php
index ff8b456..53a669b 100644
--- a/lib/SparkPost/SparkPost.php
+++ b/lib/SparkPost/SparkPost.php
@@ -2,151 +2,143 @@
namespace SparkPost;
-use Ivory\HttpAdapter\Configuration;
-use Ivory\HttpAdapter\HttpAdapterInterface;
+use Http\Client\HttpClient;
+use Http\Client\HttpAsyncClient;
+use GuzzleHttp\Psr7\Request as Request;
class SparkPost
{
- public $transmission;
- public $messageEvents;
-
- /**
- * Library version, used for setting User-Agent.
- */
- private $version = '1.2.1';
-
- /**
- * Connection config for making requests.
- */
- private $config;
-
- /**
- * @var \Ivory\HttpAdapter\HttpAdapterInterface to make requests through.
- */
- public $httpAdapter;
-
- /**
- * Default config values. Passed in values will override these.
- */
- private static $apiDefaults = [
+ private $version = '2.0.0';
+ public $httpClient;
+ private $options;
+
+ private static $defaultOptions = [
'host' => 'api.sparkpost.com',
'protocol' => 'https',
'port' => 443,
- 'strictSSL' => true,
'key' => '',
'version' => 'v1',
- 'timeout' => 10
+ 'timeout' => 10,
+ 'async' => true
];
- /**
- * Sets up httpAdapter and config.
- *
- * Sets up instances of sub libraries.
- *
- * @param \Ivory\HttpAdapter\HttpAdapterInterface $httpAdapter - An adapter for making http requests
- * @param string | array $settingsConfig - Hashmap that contains config values
- * for the SDK to connect to SparkPost. If its a string we assume that
- * its just they API Key.
- */
- public function __construct($httpAdapter, $settingsConfig)
+ public $transmissions;
+
+ public function __construct(HttpClient $httpClient, $options)
{
- //config needs to be setup before adapter because of default adapter settings
- $this->setConfig($settingsConfig);
- $this->setHttpAdapter($httpAdapter);
+ $this->setOptions($options);
+ $this->setHttpClient($httpClient);
+ $this->setupEndpoints();
+ }
- $this->transmission = new Transmission($this);
- $this->messageEvents = new MessageEvents($this);
+ public function request($method = 'GET', $uri = '', $payload = [], $headers = []) {
+ if ($this->options['async'] === true && $this->httpClient instanceof HttpAsyncClient) {
+ $this->asyncRequest($method, $uri, $payload, $headers);
+ }
+ else {
+ $this->syncRequest($method, $uri, $payload, $headers);
+ }
}
- /**
- * Creates an unwrapped api interface for endpoints that aren't yet supported.
- * The new resource is attached to this object as well as returned.
- *
- * @param string $endpoint
- *
- * @return APIResource - the unwrapped resource
- */
- public function setupUnwrapped($endpoint)
+ public function syncRequest($method = 'GET', $uri = '', $payload = [], $headers = [])
{
- $this->{$endpoint} = new APIResource($this);
- $this->{$endpoint}->endpoint = $endpoint;
+ $request = $this->buildRequest($method, $uri, $payload, $headers);
+ try
+ {
+ return new SparkPostResponse($this->httpClient->sendRequest($request));
+ }
+ catch (\Exception $exception)
+ {
+ throw new SparkPostException($exception);
+ }
+ }
- return $this->{$endpoint};
+ public function asyncRequest($method = 'GET', $uri = '', $payload = [], $headers = [])
+ {
+ if ($this->httpClient instanceof HttpAsyncClient) {
+ $request = $this->buildRequest($method, $uri, $payload, $headers);
+ return new SparkPostPromise($this->httpClient->sendAsyncRequest($request));
+ }
+ else {
+ throw new Exception('Your http client can not send asynchronous requests.');
+ }
}
- /**
- * Merges passed in headers with default headers for http requests.
- */
- public function getHttpHeaders()
+ private function buildRequest($method, $uri, $payload, $headers)
{
- $defaultOptions = [
- 'Authorization' => $this->config['key'],
- 'Content-Type' => 'application/json',
- ];
+
+ $method = trim(strtoupper($method));
+
+ if ($method === 'GET'){
+ $params = $payload;
+ $body = [];
+ }
+ else {
+ $params = [];
+ $body = $payload;
+ }
- return $defaultOptions;
+ $url = $this->getUrl($uri, $params);
+ $headers = $this->getHttpHeaders($headers);
+
+ return new Request($method, $url, $headers, json_encode($body));
}
- /**
- * Helper function for getting the configuration for http requests.
- *
- * @param array $config
- *
- * @return Configuration
- */
- private function getHttpConfig($config)
+ public function getHttpHeaders($headers = [])
{
- // create Configuration for http adapter
- $httpConfig = new Configuration();
- $baseUrl = $config['protocol'].'://'.$config['host'].($config['port'] ? ':'.$config['port'] : '').'/api/'.$config['version'];
- $httpConfig->setBaseUri($baseUrl);
- $httpConfig->setTimeout($this->config['timeout']);
- $httpConfig->setUserAgent('php-sparkpost/'.$this->version);
-
- return $httpConfig;
+ $constantHeaders = [
+ 'Authorization' => $this->options['key'],
+ 'Content-Type' => 'application/json'
+ ];
+
+ foreach ($constantHeaders as $key => $value) {
+ $headers[$key] = $value;
+ }
+
+ return $headers;
}
- /**
- * Validates and sets up the httpAdapter.
- *
- * @param $httpAdapter \Ivory\HttpAdapter\HttpAdapterInterface to make requests through.
- *
- * @throws \Exception
- */
- public function setHttpAdapter(HttpAdapterInterface $httpAdapter)
+ public function getUrl($path, $params) {
+ $options = $this->options;
+
+ for ($index = 0; $index < count($params); $index++) {
+ if (is_array($params[$index]))
+ $params[$index] = implode(',', $params);
+ }
+
+ $paramsString = http_build_query($params);
+
+ return $options['protocol'].'://'.$options['host'].($options['port'] ? ':'.$options['port'] : '').'/api/'.$options['version'].'/'.$path.($paramsString ? '?'.$paramsString : '');
+ }
+
+ public function setHttpClient(HttpClient $httpClient)
{
- $this->httpAdapter = $httpAdapter;
- $this->httpAdapter->setConfiguration($this->getHttpConfig($this->config));
+ $this->httpClient = $httpClient;
}
- /**
- * Allows the user to pass in values to override the defaults and set their API key.
- *
- * @param string | array $settingsConfig - Hashmap that contains config values
- * for the SDK to connect to SparkPost. If its a string we assume that
- * its just they API Key.
- *
- * @throws \Exception
- */
- public function setConfig($settingsConfig)
+ public function setOptions($options)
{
- // if the config map is a string we should assume that its an api key
- if (is_string($settingsConfig)) {
- $settingsConfig = ['key' => $settingsConfig];
+ // if the options map is a string we should assume that its an api key
+ if (is_string($options)) {
+ $options = ['key' => $options];
}
// Validate API key because its required
- if (!isset($settingsConfig['key']) || !preg_match('/\S/', $settingsConfig['key'])) {
+ if (!isset($this->options['key']) && (!isset($options['key']) || !preg_match('/\S/', $options['key']))) {
throw new \Exception('You must provide an API key');
}
- $this->config = self::$apiDefaults;
+ $this->options = isset($this->options) ? $this->options : self::$defaultOptions;
- // set config, overriding defaults
- foreach ($settingsConfig as $configOption => $configValue) {
- if (key_exists($configOption, $this->config)) {
- $this->config[$configOption] = $configValue;
+ // set options, overriding defaults
+ foreach ($options as $option => $value) {
+ if (key_exists($option, $this->options)) {
+ $this->options[$option] = $value;
}
}
}
+
+ private function setupEndpoints() {
+ $this->transmissions = new Transmission($this);
+ }
}
diff --git a/lib/SparkPost/SparkPostException.php b/lib/SparkPost/SparkPostException.php
new file mode 100644
index 0000000..92be3d5
--- /dev/null
+++ b/lib/SparkPost/SparkPostException.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SparkPost;
+
+use Http\Client\Exception\HttpException as HttpException;
+
+class SparkPostException extends \Exception {
+
+ private $body = null;
+
+ public function __construct(\Exception $exception) {
+ $message = $exception->getMessage();
+ if($exception instanceof HttpException) {
+ $message = $exception->getResponse()->getBody()->__toString();
+ $this->body = json_decode($message, true);
+ }
+
+ parent::__construct($message, $exception->getCode(), $exception->getPrevious());
+ }
+
+ public function getBody() {
+ return $this->body;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/lib/SparkPost/SparkPostPromise.php b/lib/SparkPost/SparkPostPromise.php
new file mode 100644
index 0000000..462599a
--- /dev/null
+++ b/lib/SparkPost/SparkPostPromise.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SparkPost;
+
+use Http\Promise\Promise as HttpPromise;
+
+class SparkPostPromise implements HttpPromise
+{
+ private $promise;
+
+ public function __construct(HttpPromise $promise) {
+ $this->promise = $promise;
+ }
+
+ public function then(callable $onFulfilled = null, callable $onRejected = null) {
+ $this->promise->then(
+ function($response) {
+ $onFulfilled(new SparkPostResponse($response));
+ },
+ function(\Exception $exception) {
+ $onRejected(new SparkPostException($exception));
+ });
+ }
+
+ public function getState() {
+ return $this->promise->getState();
+ }
+
+
+ public function wait($unwrap = true) {
+ try
+ {
+ $response = $this->promise->wait($unwrap);
+ return new SparkPostResponse($response);
+ }
+ catch (\Exception $exception)
+ {
+ throw new SparkPostException($exception);
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/lib/SparkPost/SparkPostResponse.php b/lib/SparkPost/SparkPostResponse.php
new file mode 100644
index 0000000..161b864
--- /dev/null
+++ b/lib/SparkPost/SparkPostResponse.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SparkPost;
+
+use Psr\Http\Message\ResponseInterface as ResponseInterface;
+use Psr\Http\Message\StreamInterface as StreamInterface;
+
+class SparkPostResponse implements ResponseInterface {
+
+ private $response;
+
+ public function __construct(ResponseInterface $response) {
+ $this->response = $response;
+ }
+
+ public function getBody()
+ {
+ $body = $this->response->getBody();
+ $body_string = $body->__toString();
+
+ if (is_string($body_string)) {
+ $json = json_decode($body_string, true);
+
+ if (json_last_error() == JSON_ERROR_NONE) {
+ return $json;
+ }
+ else {
+ return $body;
+ }
+ }
+
+ return $body;
+ }
+
+ // pass these down to the response given in the constructor
+
+ public function getProtocolVersion()
+ {
+ return $this->response->getProtocolVersion();
+ }
+
+ public function withProtocolVersion($version)
+ {
+ return $this->response->withProtocolVersion($version);
+ }
+
+ public function getHeaders()
+ {
+ return $this->response->getHeaders();
+ }
+
+ public function hasHeader($name)
+ {
+ return $this->response->hasHeader($name);
+ }
+
+ public function getHeader($name)
+ {
+ return $this->response->getHeader($name);
+ }
+
+ public function getHeaderLine($name)
+ {
+ return $this->response->getHeaderLine($name);
+ }
+
+ public function withHeader($name, $value)
+ {
+ return $this->response->withHeader($name, $value);
+ }
+
+ public function withAddedHeader($name, $value)
+ {
+ return $this->response->withAddedHeader($name, $value);
+ }
+
+ public function withoutHeader($name)
+ {
+ return $this->response->withoutHeader($name);
+ }
+
+ public function withBody(StreamInterface $body)
+ {
+ return $this->response->withBody($body);
+ }
+
+ public function getStatusCode()
+ {
+ return $this->response->getStatusCode();
+ }
+
+ public function withStatus($code, $reasonPhrase = '')
+ {
+ return $this->response->withStatus($code, $reasonPhrase);
+ }
+
+ public function getReasonPhrase()
+ {
+ $this->response->getReasonPhrase();
+ }
+
+}
+
+?> \ No newline at end of file