summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/unwrapped/create_template.php25
-rw-r--r--lib/SparkPost/APIResource.php216
-rw-r--r--lib/SparkPost/SparkPost.php4
-rw-r--r--lib/SparkPost/Transmission.php145
-rw-r--r--test/unit/APIResourceTest.php144
-rw-r--r--test/unit/SparkPostTest.php1
-rw-r--r--test/unit/TransmissionTest.php4
7 files changed, 410 insertions, 129 deletions
diff --git a/examples/unwrapped/create_template.php b/examples/unwrapped/create_template.php
new file mode 100644
index 0000000..717430f
--- /dev/null
+++ b/examples/unwrapped/create_template.php
@@ -0,0 +1,25 @@
+<?php
+namespace Examples\Unwrapped;
+require_once (dirname(__FILE__).'/../bootstrap.php');
+use SparkPost\SparkPost;
+use SparkPost\APIResource;
+
+$key = 'YOURAPIKEY';
+SparkPost::setConfig(array('key'=>$key));
+
+try {
+ // define the endpoint
+ APIResource::$endpoint = 'templates';
+
+ $templateConfig = array(
+ 'name' => 'Summer Sale!',
+ 'content.from' => 'marketing@bounces.company.example',
+ 'content.subject' => 'Summer deals',
+ 'content.html' => '<b>Check out these deals!</b>',
+ );
+ $results = APIResource::sendRequest($templateConfig);
+ echo 'Congrats you can use your SDK!';
+} catch (\Exception $exception) {
+ echo $exception->getMessage();
+}
+?> \ No newline at end of file
diff --git a/lib/SparkPost/APIResource.php b/lib/SparkPost/APIResource.php
new file mode 100644
index 0000000..469ba7e
--- /dev/null
+++ b/lib/SparkPost/APIResource.php
@@ -0,0 +1,216 @@
+<?php
+namespace SparkPost;
+use Guzzle\Http\Client;
+use Guzzle\Http\Exception\ClientErrorResponseException;
+
+/**
+ * @desc SDK interface for managing SparkPost API endpoints
+ */
+class APIResource {
+
+ /**
+ * @desc name of the API endpoint, mainly used for URL construction.
+ * @var string
+ */
+ public static $endpoint;
+
+ /**
+ * @desc singleton holder to create a guzzle http client
+ * @var \GuzzleHttp\Client
+ */
+ protected static $request;
+
+ /**
+ * @desc Mapping for values passed into the send method to the values needed for the respective API
+ * @var array
+ */
+ protected static $parameterMappings = array();
+
+ /**
+ * @desc Sets up default structure and default values for the model that is acceptable by the API
+ * @var array
+ */
+ protected static $structure = array();
+
+ /**
+ * @desc Ensure that this class cannot be instansiated
+ */
+ private function __construct() {}
+
+ /**
+ * @desc Creates and returns a guzzle http client.
+ * @return \GuzzleHttp\Client
+ */
+ protected static function getHttpClient() {
+ if(!isset(self::$request)) {
+ self::$request = new Client();
+ }
+ return self::$request;
+ }
+
+ /**
+ * @desc Private Method helper to get the configuration values to create the base url for the current API endpoint
+ *
+ * @return string base url for the transmissions API
+ */
+ protected static function getBaseUrl($config) {
+ $baseUrl = '/api/' . $config['version'] . '/' . static::$endpoint;
+ return $config['protocol'] . '://' . $config['host'] . ($config['port'] ? ':' . $config['port'] : '') . $baseUrl;
+ }
+
+
+ /**
+ * @desc Private Method helper to reference parameter mappings and set the right value for the right parameter
+ */
+ protected static 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;
+
+ }
+
+ protected static function buildRequestModel( $requestConfig, $model=array() ) {
+ foreach($requestConfig as $key=>$value) {
+ self::setMappedValue($model, $key, $value);
+ }
+ return $model;
+ }
+
+ /**
+ * @desc Method for issuing POST requests
+ *
+ * @return array API repsonse represented as key-value pairs
+ */
+ public static function sendRequest( $requestConfig ) {
+ $hostConfig = SparkPost::getConfig();
+ $request = self::getHttpClient();
+
+ //create model from $transmissionConfig
+ $model = static::$structure;
+ $requestModel = self::buildRequestModel( $requestConfig, $model );
+
+ //send the request
+ try {
+ $response = $request->post(
+ self::getBaseUrl($hostConfig),
+ array('authorization' => $hostConfig['key']),
+ json_encode($requestModel),
+ array("verify"=>$hostConfig['strictSSL'])
+ )->send();
+
+ return $response->json();
+ }
+ /*
+ * Handles 4XX responses
+ */
+ catch (ClientErrorResponseException $exception) {
+ $response = $exception->getResponse();
+ $responseArray = $response->json();
+ throw new \Exception(json_encode($responseArray['errors']));
+ }
+ /*
+ * Handles 5XX Errors, Configuration Errors, and a catch all for other errors
+ */
+ catch (\Exception $exception) {
+ throw new \Exception("Unable to contact ".ucfirst(static::$endpoint)." API: ". $exception->getMessage());
+ }
+ }
+
+
+ /**
+ * @desc Wrapper method for issuing GET request to current API endpoint
+ *
+ * @param string $resourcePath (optional) string resource path of specific resource
+ * @param array $options (optional) query string parameters
+ * @return array Result set of transmissions found
+ */
+ public static function fetchResource( $resourcePath=null, $options=array() ) {
+ return self::callResource( 'get', $resourcePath, $options );
+ }
+
+ /**
+ * @desc Wrapper method for issuing DELETE request to current API endpoint
+ *
+ * @param string $resourcePath (optional) string resource path of specific resource
+ * @param array $options (optional) query string parameters
+ * @return array Result set of transmissions found
+ */
+ public static function deleteResource( $resourcePath=null, $options=array() ) {
+ return self::callResource( 'delete', $resourcePath, $options );
+ }
+
+ /**
+ * @desc 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
+ */
+ private static function callResource( $action, $resourcePath=null, $options=array() ) {
+
+ if( !in_array( $action, array('get', 'delete') ) ) throw new \Exception('Invalid resource action');
+
+ //build the url
+ $hostConfig = SparkPost::getConfig();
+ $url = self::getBaseUrl($hostConfig);
+ if (!is_null($resourcePath)){
+ $url .= '/'.$resourcePath;
+ }
+
+ // untested:
+ if( !empty($options) ) {
+ $queryString = http_build_query($options);
+ $url .= '?'.$queryString;
+ }
+
+ $request = self::getHttpClient();
+
+ //make request
+ try {
+ $response = $request->{$action}($url, array('authorization' => $hostConfig['key']), array("verify"=>$hostConfig['strictSSL']))->send();
+ return $response->json();
+ }
+ /*
+ * Handles 4XX responses
+ */
+ catch (ClientErrorResponseException $exception) {
+ $response = $exception->getResponse();
+ $statusCode = $response->getStatusCode();
+ if($statusCode === 404) {
+ throw new \Exception("The specified resource does not exist", 404);
+ }
+ throw new \Exception("Received bad response from ".ucfirst(static::$endpoint)." API: ". $statusCode );
+ }
+ /*
+ * Handles 5XX Errors, Configuration Errors, and a catch all for other errors
+ */
+ catch (\Exception $exception) {
+ throw new \Exception("Unable to contact ".ucfirst(static::$endpoint)." API: ". $exception->getMessage());
+ }
+ }
+
+}
diff --git a/lib/SparkPost/SparkPost.php b/lib/SparkPost/SparkPost.php
index 5c2a554..755df46 100644
--- a/lib/SparkPost/SparkPost.php
+++ b/lib/SparkPost/SparkPost.php
@@ -51,6 +51,10 @@ class SparkPost {
}
return self::$config;
}
+
+ public static function unsetConfig() {
+ self::$config = NULL;
+ }
}
?> \ No newline at end of file
diff --git a/lib/SparkPost/Transmission.php b/lib/SparkPost/Transmission.php
index dcc3fe1..4e54f1d 100644
--- a/lib/SparkPost/Transmission.php
+++ b/lib/SparkPost/Transmission.php
@@ -6,18 +6,15 @@ use Guzzle\Http\Exception\ClientErrorResponseException;
/**
* @desc SDK interface for managing transmissions
*/
-class Transmission {
- /**
- * @desc singleton holder to create a guzzle http client
- * @var \GuzzleHttp\Client
- */
- private static $request;
+class Transmission extends APIResource {
+
+ public static $endpoint = 'transmissions';
/**
* @desc Mapping for values passed into the send method to the values needed for the Transmission API
* @var array
*/
- private static $parameterMappings = array(
+ protected static $parameterMappings = array(
'campaign'=>'campaign_id',
'metadata'=>'metadata',
'substitutionData'=>'substitution_data',
@@ -42,7 +39,7 @@ class Transmission {
* @desc Sets up default structure and default values for the model that is acceptable by the API
* @var array
*/
- private static $structure = array(
+ protected static $structure = array(
'return_path'=>"default@sparkpostmail.com",
'content'=>array(
'html'=>null,
@@ -53,48 +50,6 @@ class Transmission {
);
/**
- * @desc Ensure that this class cannot be instansiated
- */
- private function __construct() {}
-
- /**
- * @desc Creates and returns a guzzle http client.
- * @return \GuzzleHttp\Client
- */
- private static function getHttpClient() {
- if(!isset(self::$request)) {
- self::$request = new Client();
- }
- return self::$request;
- }
-
-
- /**
- * @desc Private Method helper to reference parameter mappings and set the right value for the right parameter
- */
- private static function setMappedValue (&$model, $mapKey, $value) {
- //get mapping
- if(array_key_exists($mapKey, self::$parameterMappings)) {
- $temp = &$model;
- $path = explode('.', self::$parameterMappings[$mapKey]);
- foreach( $path as $key ) {
- $temp = &$temp[$key];
- }
- $temp = $value;
- } //ignore anything we don't have a mapping for
- }
-
- /**
- * @desc Private Method helper to get the configuration values to create the base url for the transmissions API
- *
- * @return string base url for the transmissions API
- */
- private static function getBaseUrl($config) {
- $baseUrl = '/api/' . $config['version'] . '/transmissions';
- return $config['protocol'] . '://' . $config['host'] . ($config['port'] ? ':' . $config['port'] : '') . $baseUrl;
- }
-
- /**
* @desc Method for issuing POST request to the Transmissions API
*
* This method assumes that all the appropriate fields have
@@ -120,80 +75,8 @@ class Transmission {
*
* @return array API repsonse represented as key-value pairs
*/
- public static function send($transmissionConfig) {
- $hostConfig = SparkPost::getConfig();
- $request = self::getHttpClient();
-
- //create model from $transmissionConfig
- $model = self::$structure;
- foreach($transmissionConfig as $key=>$value) {
- self::setMappedValue($model, $key, $value);
- }
-
- //send the request
- try {
- $response = $request->post(self::getBaseUrl($hostConfig), array('authorization' => $hostConfig['key']), json_encode($model), array("verify"=>$hostConfig['strictSSL']))->send();
- return $response->json();
- }
- /*
- * Handles 4XX responses
- */
- catch (ClientErrorResponseException $exception) {
- $response = $exception->getResponse();
- $responseArray = $response->json();
- throw new \Exception(json_encode($responseArray['errors']));
- }
- /*
- * Handles 5XX Errors, Configuration Errors, and a catch all for other errors
- */
- catch (\Exception $exception) {
- throw new \Exception('Unable to contact Transmissions API: '. $exception->getMessage());
- }
- }
-
- /**
- * @desc Private Method for issuing GET request to Transmissions API
- *
- * This method is responsible for getting the collection _and_
- * a specific entity from the Transmissions API
- *
- * If TransmissionID parameter is omitted, then we fetch the collection
- *
- * @param string $transmissionID (optional) string Transmission ID of specific Transmission to retrieve
- * @return array Result set of transmissions found
- */
- private static function fetch ($transmissionID = null) {
- //figure out the url
- $hostConfig = SparkPost::getConfig();
- $url = self::getBaseUrl($hostConfig);
- if (!is_null($transmissionID)){
- $url .= '/'.$transmissionID;
- }
-
- $request = self::getHttpClient();
-
- //make request
- try {
- $response = $request->get($url, array('authorization' => $hostConfig['key']), array("verify"=>$hostConfig['strictSSL']))->send();
- return $response->json();
- }
- /*
- * Handles 4XX responses
- */
- catch (ClientErrorResponseException $exception) {
- $response = $exception->getResponse();
- $statusCode = $response->getStatusCode();
- if($statusCode === 404) {
- throw new \Exception("The specified Transmission ID does not exist", 404);
- }
- throw new \Exception("Received bad response from Transmission API: ". $statusCode);
- }
- /*
- * Handles 5XX Errors, Configuration Errors, and a catch all for other errors
- */
- catch (\Exception $exception) {
- throw new \Exception('Unable to contact Transmissions API: '. $exception->getMessage());
- }
+ public static function send( $transmissionConfig ) {
+ return self::sendRequest( $transmissionConfig );
}
/**
@@ -202,8 +85,12 @@ class Transmission {
*
* @return array result Set of transmissions
*/
- public static function all() {
- return self::fetch();
+ public static function all( $campaignID=null, $templateID=null ) {
+ $options = array();
+ if( $campaignID !== NULL ) $options['campaign_id'] = $campaignID;
+ if( $templateID !== NULL ) $options['template_id'] = $templateID;
+
+ return self::fetchResource( null, $options );
}
/**
@@ -214,7 +101,11 @@ class Transmission {
* @return array result Single transmission represented in key-value pairs
*/
public static function find($transmissionID) {
- return self::fetch($transmissionID);
+ return self::fetchResource($transmissionID);
+ }
+
+ public static function delete( $transmissionID ) {
+ return self::deleteResource($transmissionID);
}
}
diff --git a/test/unit/APIResourceTest.php b/test/unit/APIResourceTest.php
new file mode 100644
index 0000000..fc85fa3
--- /dev/null
+++ b/test/unit/APIResourceTest.php
@@ -0,0 +1,144 @@
+<?php
+namespace SparkPost\Test;
+
+use SparkPost\APIResource;
+use SparkPost\SparkPost;
+use Guzzle\Plugin\Mock\MockPlugin;
+use Guzzle\Http\Message\Response;
+
+
+class APIResourceTest extends \PHPUnit_Framework_TestCase {
+
+ private $client = null;
+
+ /**
+ * Allows access to private methods
+ *
+ * This is needed to mock the GuzzleHttp\Client responses
+ *
+ * @param string $name
+ * @return ReflectionMethod
+ */
+ private static function getMethod($name) {
+ $class = new \ReflectionClass('\SparkPost\APIResource');
+ $method = $class->getMethod($name);
+ $method->setAccessible(true);
+ return $method;
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @before
+ * @see PHPUnit_Framework_TestCase::setUp()
+ */
+ public function setUp() {
+ SparkPost::setConfig(array('key'=>'blah'));
+ $this->client = self::getMethod('getHttpClient')->invoke(null); //so we can bootstrap api responses
+ APIResource::$endpoint = 'someValidEndpoint'; // when using APIResource directly an endpoint needs to be set.
+ }
+
+ /**
+ * @desc Ensures that the configuration class is not instantiable.
+ */
+ public function testConstructorCannotBeCalled() {
+ $class = new \ReflectionClass('\SparkPost\Transmission');
+ $this->assertFalse($class->isInstantiable());
+ }
+
+ /**
+ * @desc tests happy path
+ */
+ public function testFetchWithGoodResponse() {
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(200, array(), '{"results":[{"test":"This is a test"}, {"test":"two"}]}'));
+ $this->client->addSubscriber($mock);
+ $this->assertEquals(array("results"=>array(array('test'=>'This is a test'), array('test'=>'two'))), APIResource::fetchResource());
+ }
+
+ /**
+ * @desc tests happy path
+ */
+ public function testDeleteWithGoodResponse() {
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(200, array(), '{"results":[{"test":"This is a test"}]}'));
+ $this->client->addSubscriber($mock);
+
+ $this->assertEquals(array("results"=>array(array('test'=>'This is a test'))), APIResource::deleteResource('someId'));
+ }
+
+ /**
+ * @desc tests 404 bad response
+ * @expectedException Exception
+ * @expectedExceptionMessage The specified resource does not exist
+ */
+ public function testFetchWith404Response() {
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(404, array()));
+ $this->client->addSubscriber($mock);
+ APIResource::fetchResource('someId');
+ }
+
+ /**
+ * @desc tests unknown bad response
+ * @expectedException Exception
+ * @expectedExceptionMessage Received bad response from SomeValidEndpoint API: 400
+ */
+ public function testFetchWithOtherBadResponse() {
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(400, array()));
+ $this->client->addSubscriber($mock);
+ APIResource::fetchResource('someId');
+ }
+
+ /**
+ * @desc tests bad response
+ * @expectedException Exception
+ * @expectedExceptionMessageRegExp /Unable to contact SomeValidEndpoint API:.* /
+ */
+ public function testFetchForCatchAllException() {
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(500));
+ $this->client->addSubscriber($mock);
+ APIResource::fetchResource('someId');
+ }
+
+ /**
+ * @desc tests happy path
+ */
+ public function testSuccessfulSend() {
+ $body = array("result"=>array("transmission_id"=>"11668787484950529"), "status"=>array("message"=> "ok","code"=> "1000"));
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(200, array(), json_encode($body)));
+ $this->client->addSubscriber($mock);
+
+
+ $this->assertEquals($body, APIResource::sendRequest(array('text'=>'awesome email')));
+ }
+
+ /**
+ * @desc tests bad response
+ * @expectedException Exception
+ * @expectedExceptionMessage ["This is a fake error"]
+ */
+ public function testSendFor400Exception() {
+ $body = array('errors'=>array('This is a fake error'));
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(400, array(), json_encode($body)));
+ $this->client->addSubscriber($mock);
+ APIResource::sendRequest(array('text'=>'awesome email'));
+ }
+
+
+ /**
+ * @desc tests bad response
+ * @expectedException Exception
+ * @expectedExceptionMessageRegExp /Unable to contact SomeValidEndpoint API:.* /
+ */
+ public function testSendForCatchAllException() {
+ $mock = new MockPlugin();
+ $mock->addResponse(new Response(500));
+ $this->client->addSubscriber($mock);
+ APIResource::sendRequest(array('text'=>'awesome email'));
+ }
+
+}
diff --git a/test/unit/SparkPostTest.php b/test/unit/SparkPostTest.php
index 650cb36..f40461d 100644
--- a/test/unit/SparkPostTest.php
+++ b/test/unit/SparkPostTest.php
@@ -20,6 +20,7 @@ class SparkPostTest extends \PHPUnit_Framework_TestCase {
* @expectedExceptionMessage No configuration has been provided
*/
public function testGetConfigEmptyException() {
+ SparkPost::unsetConfig();
SparkPost::getConfig();
}
diff --git a/test/unit/TransmissionTest.php b/test/unit/TransmissionTest.php
index de72f2a..ca30b4a 100644
--- a/test/unit/TransmissionTest.php
+++ b/test/unit/TransmissionTest.php
@@ -68,7 +68,7 @@ class TransmissionTest extends \PHPUnit_Framework_TestCase {
/**
* @desc tests 404 bad response
* @expectedException Exception
- * @expectedExceptionMessage The specified Transmission ID does not exist
+ * @expectedExceptionMessage The specified resource does not exist
*/
public function testFindWith404Response() {
$mock = new MockPlugin();
@@ -80,7 +80,7 @@ class TransmissionTest extends \PHPUnit_Framework_TestCase {
/**
* @desc tests unknown bad response
* @expectedException Exception
- * @expectedExceptionMessage Received bad response from Transmission API: 400
+ * @expectedExceptionMessage Received bad response from Transmissions API: 400
*/
public function testFindWithOtherBadResponse() {
$mock = new MockPlugin();