diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/SendGridCompatibility/SendGrid.php | 16 | ||||
-rw-r--r-- | lib/SparkPost/APIResource.php | 236 | ||||
-rw-r--r-- | lib/SparkPost/SparkPost.php | 185 | ||||
-rw-r--r-- | lib/SparkPost/Transmission.php | 64 |
4 files changed, 287 insertions, 214 deletions
diff --git a/lib/SendGridCompatibility/SendGrid.php b/lib/SendGridCompatibility/SendGrid.php index a85337a..b92457b 100644 --- a/lib/SendGridCompatibility/SendGrid.php +++ b/lib/SendGridCompatibility/SendGrid.php @@ -1,22 +1,24 @@ <?php namespace SparkPost\SendGridCompatibility; -use SparkPost\Transmission; +use SparkPost\SparkPost; use SparkPost\SendGridCompatibility\Email; -use SparkPost\Configuration; class SendGrid{ - public function __construct($username, $password, $options = null) { + private $sparky; + + public function __construct($username, $password, $options = null, $httpAdapter) { //username isn't used in our system $opts = array('key'=>$password); if (!is_null($options)) { $opts = array_merge($opts, $options); } - Configuration::setConfig($opts); + + $this->sparky = new SparkPost($httpAdapter, $opts); } - + public function send(Email $email) { - Trasmission::send($email->toSparkPostTransmission()); + $this->sparky->transmission->send($email->toSparkPostTransmission()); } } -?>
\ No newline at end of file +?> diff --git a/lib/SparkPost/APIResource.php b/lib/SparkPost/APIResource.php index 469ba7e..4c4cf08 100644 --- a/lib/SparkPost/APIResource.php +++ b/lib/SparkPost/APIResource.php @@ -1,68 +1,54 @@ <?php namespace SparkPost; -use Guzzle\Http\Client; -use Guzzle\Http\Exception\ClientErrorResponseException; +use Ivory\HttpAdapter\HttpAdapterException; +use SparkPost\SparkPost; /** * @desc SDK interface for managing SparkPost API endpoints */ class APIResource { - + /** * @desc name of the API endpoint, mainly used for URL construction. + * This is public to provide an interface + * * @var string */ - public static $endpoint; - - /** - * @desc singleton holder to create a guzzle http client - * @var \GuzzleHttp\Client - */ - protected static $request; - + public $endpoint; + /** * @desc Mapping for values passed into the send method to the values needed for the respective API * @var array */ - protected static $parameterMappings = array(); - + protected static $parameterMappings = []; + /** * @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() {} + protected static $structure = []; + + /** + * @desc SparkPost reference for httpAdapters and configs + */ + protected $sparkpost; /** - * @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 + * @desc Initializes config and httpAdapter for use later. + * @param $sparkpost SparkPost\SparkPost provides api configuration information */ - protected static function getBaseUrl($config) { - $baseUrl = '/api/' . $config['version'] . '/' . static::$endpoint; - return $config['protocol'] . '://' . $config['host'] . ($config['port'] ? ':' . $config['port'] : '') . $baseUrl; - } - - + public function __construct(SparkPost $sparkpost) { + $this->sparkpost = $sparkpost; + } + /** * @desc 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 static function setMappedValue (&$model, $mapKey, $value) { + protected function setMappedValue (&$model, $mapKey, $value) { //get mapping if( empty(static::$parameterMappings) ) { // if parameterMappings is empty we can assume that no wrapper is defined @@ -74,7 +60,7 @@ class APIResource { } else { return; } - + $path = explode('.', $mapPath); $temp = &$model; foreach( $path as $key ) { @@ -84,79 +70,100 @@ class APIResource { $temp = &$temp[$key]; } $temp = $value; - + } - - protected static function buildRequestModel( $requestConfig, $model=array() ) { - foreach($requestConfig as $key=>$value) { - self::setMappedValue($model, $key, $value); + + /** + * @desc maps values from the passed in model to those needed for the request + * @param $requestConfig the passed in model + * @param $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; } - + /** - * @desc Method for issuing POST requests - * - * @return array API repsonse represented as key-value pairs + * @desc posts to the api with a supplied body + * @param body post body for the request + * @return array Result of the request */ - 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()); - } + public function create(Array $body=[]) { + return $this->callResource( 'post', null, ['body'=>$body]); + } + + /** + * @desc Makes a put request to the api with a supplied body + * @param body Put body for the request + * @return array Result of the request + */ + public function update( $resourcePath, Array $body=[]) { + return $this->callResource( 'put', $resourcePath, ['body'=>$body]); } - /** * @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 + * @return array Result of the request */ - public static function fetchResource( $resourcePath=null, $options=array() ) { - return self::callResource( 'get', $resourcePath, $options ); + public function get( $resourcePath=null, Array $query=[] ) { + return $this->callResource( 'get', $resourcePath, ['query'=>$query] ); } - + /** * @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 + * @return array Result of the request */ - public static function deleteResource( $resourcePath=null, $options=array() ) { - return self::callResource( 'delete', $resourcePath, $options ); + public function delete( $resourcePath=null, Array $query=[] ) { + return $this->callResource( 'delete', $resourcePath, ['query'=>$query] ); } - + + + /** + * @desc 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. + * @return string the assembled URL + */ + private function buildUrl($resourcePath, $options) { + $url = join(['/', $this->endpoint, '/']); + if (!is_null($resourcePath)){ + $url .= $resourcePath; + } + + if( !empty($options['query'])) { + $queryString = http_build_query($options['query']); + $url .= '?'.$queryString; + } + + return $url; + } + + + /** + * @desc 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; + } + + /** * @desc Private Method for issuing GET and DELETE request to current API endpoint * @@ -170,47 +177,38 @@ class APIResource { * @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; + private function callResource( $action, $resourcePath=null, $options=[] ) { + $action = strtoupper($action); // normalize + + if( !in_array($action, ['POST', 'PUT', 'GET', 'DELETE'])) { + throw new \Exception('Invalid resource action'); } - - $request = self::getHttpClient(); - + + $url = $this->buildUrl($resourcePath, $options); + $body = $this->buildBody($options); + //make request try { - $response = $request->{$action}($url, array('authorization' => $hostConfig['key']), array("verify"=>$hostConfig['strictSSL']))->send(); - return $response->json(); + $response = $this->sparkpost->httpAdapter->send($url, $action, $this->sparkpost->getHttpHeaders(), $body); + return json_decode($response->getBody()->getContents(), true); } /* * Handles 4XX responses - */ - catch (ClientErrorResponseException $exception) { - $response = $exception->getResponse(); - $statusCode = $response->getStatusCode(); + */ + catch (HttpAdapterException $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 ); + throw new \Exception("Received bad response from ".ucfirst($this->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()); + throw new \Exception("Unable to contact ".ucfirst($this->endpoint)." API: ". $exception->getMessage()); } } - + } diff --git a/lib/SparkPost/SparkPost.php b/lib/SparkPost/SparkPost.php index 755df46..83200c0 100644 --- a/lib/SparkPost/SparkPost.php +++ b/lib/SparkPost/SparkPost.php @@ -1,60 +1,137 @@ <?php namespace SparkPost; +use Ivory\HttpAdapter\Configuration; +use Ivory\HttpAdapter\HttpAdapterInterface; class SparkPost { - - private static $config; - private static $defaults = array( - 'host'=>'api.sparkpost.com', - 'protocol'=>'https', - 'port'=>443, - 'strictSSL'=>true, - 'key'=>'', - 'version'=>'v1' - ); - - /** - * Enforce that this object can't be instansiated - */ - private function __construct(){} - - /** - * Allows the user to pass in values to override the defaults and set their API key - * @param Array $configMap - Hashmap that contains config values for the SDK to connect to SparkPost - * @throws \Exception - */ - public static function setConfig(array $configMap) { - //check for API key because its required - if (isset($configMap['key'])){ - $key = trim($configMap['key']); - if(empty($key)){ - throw new \Exception('You must provide an API key'); - } - } else { - throw new \Exception('You must provide an API key'); - } - self::$config = self::$defaults; - foreach ($configMap as $configOption => $configValue) { - if(key_exists($configOption, self::$config)) { - self::$config[$configOption] = $configValue; - } - } - } - - /** - * Retrieves the configuration that was previously setup by the user - * @throws \Exception - */ - public static function getConfig() { - if (self::$config === null) { - throw new \Exception('No configuration has been provided'); - } - return self::$config; - } - - public static function unsetConfig() { - self::$config = NULL; - } + + public $transmission; + + /** + * @dec connection config for making requests. + */ + private $config; + + /** + * @desc Ivory\HttpAdapter\HttpAdapterInterface to make requests through. + */ + public $httpAdapter; + + /** + * @desc Default config values. Passed in values will override these. + */ + private static $apiDefaults = [ + 'host'=>'api.sparkpost.com', + 'protocol'=>'https', + 'port'=>443, + 'strictSSL'=>true, + 'key'=>'', + 'version'=>'v1' + ]; + + /** + * @desc sets up httpAdapter and config + * + * Sets up instances of sub libraries. + * + * @param Ivory\HttpAdapter $httpAdapter - An adapter for making http requests + * @param Array $settingsConfig - Hashmap that contains config values for the SDK to connect to SparkPost + */ + public function __construct($httpAdapter, $settingsConfig) { + //config needs to be setup before adapter because of default adapter settings + $this->setConfig($settingsConfig); + $this->setHttpAdapter($httpAdapter); + + $this->transmission = new Transmission($this); + } + + + + /** + * Creates an unwrapped api interface for endpoints that aren't yet supported. + * The new resource is attached to this object as well as returned + * @return SparkPost\APIResource - the unwrapped resource + */ + public function setupUnwrapped ($endpoint) { + $this->{$endpoint} = new APIResource($this); + $this->{$endpoint}->endpoint = $endpoint; + + return $this->{$endpoint}; + } + + /** + * @desc Merges passed in headers with default headers for http requests + * @return Array - headers to be set on http requests + */ + public function getHttpHeaders(Array $headers = null) { + $defaultOptions = [ + 'Authorization' => $this->config['key'], + 'Content-Type' => 'application/json', + ]; + + // Merge passed in headers with defaults + if (!is_null($headers)) { + foreach ($headers as $header => $value) { + $defaultOptions[$header] = $value; + } + } + return $defaultOptions; + } + + + /** + * @desc Helper function for getting the configuration for http requests + * @return \Ivory\HttpAdapter\Configuration + */ + private function getHttpConfig($config) { + // get composer.json to extract version number + $composerFile = file_get_contents(dirname(__FILE__) . "/../../composer.json"); + $composer = json_decode($composerFile, true); + + // create Configuration for http adapter + $httpConfig = new Configuration(); + $baseUrl = $config['protocol'] . '://' . $config['host'] . ($config['port'] ? ':' . $config['port'] : '') . '/api/' . $config['version']; + $httpConfig->setBaseUri($baseUrl); + $httpConfig->setUserAgent('php-sparkpost/' . $composer['version']); + return $httpConfig; + } + + + /** + * @desc Validates and sets up the httpAdapter + * @param $httpAdapter Ivory\HttpAdapter\HttpAdapterInterface to make requests through. + * @throws \Exception + */ + public function setHttpAdapter($httpAdapter) { + if (!$httpAdapter instanceOf HttpAdapterInterface) { + throw new \Exception('$httpAdapter paramter must be a valid Ivory\HttpAdapter'); + } + + $this->httpAdapter = $httpAdapter; + $this->httpAdapter->setConfiguration($this->getHttpConfig($this->config)); + } + + + /** + * Allows the user to pass in values to override the defaults and set their API key + * @param Array $settingsConfig - Hashmap that contains config values for the SDK to connect to SparkPost + * @throws \Exception + */ + public function setConfig(Array $settingsConfig) { + // Validate API key because its required + if (!isset($settingsConfig['key']) || empty(trim($settingsConfig['key']))){ + throw new \Exception('You must provide an API key'); + } + + $this->config = self::$apiDefaults; + + // set config, overriding defaults + foreach ($settingsConfig as $configOption => $configValue) { + if(key_exists($configOption, $this->config)) { + $this->config[$configOption] = $configValue; + } + } + } } -?>
\ No newline at end of file +?> diff --git a/lib/SparkPost/Transmission.php b/lib/SparkPost/Transmission.php index 4e54f1d..3b3c056 100644 --- a/lib/SparkPost/Transmission.php +++ b/lib/SparkPost/Transmission.php @@ -7,14 +7,14 @@ use Guzzle\Http\Exception\ClientErrorResponseException; * @desc SDK interface for managing transmissions */ class Transmission extends APIResource { - - public static $endpoint = 'transmissions'; - + + public $endpoint = 'transmissions'; + /** - * @desc Mapping for values passed into the send method to the values needed for the Transmission API + * @desc Mapping for values passed into the send method to the values needed for the Transmission API * @var array */ - protected static $parameterMappings = array( + protected static $parameterMappings = [ 'campaign'=>'campaign_id', 'metadata'=>'metadata', 'substitutionData'=>'substitution_data', @@ -33,29 +33,29 @@ class Transmission extends APIResource { 'trackOpens'=>'options.open_tracking', 'trackClicks'=>'options.click_tracking', 'useDraftTemplate'=>'use_draft_template' - ); - + ]; + /** * @desc Sets up default structure and default values for the model that is acceptable by the API * @var array */ - protected static $structure = array( + protected static $structure = [ 'return_path'=>"default@sparkpostmail.com", - 'content'=>array( - 'html'=>null, + 'content'=>[ + 'html'=>null, 'text'=>null, 'email_rfc822'=>null - ), + ], 'use_draft_template'=>false - ); - + ]; + /** * @desc Method for issuing POST request to the Transmissions API * * This method assumes that all the appropriate fields have - * been populated by the user through configuration. Acceptable - * configuration values are: - * 'campaign': string, + * been populated by the user through configuration. Acceptable + * configuration values are: + * 'campaign': string, * 'metadata': array, * 'substitutionData': array, * 'description': string, @@ -71,42 +71,38 @@ class Transmission extends APIResource { * 'template': string, * 'trackOpens': boolean, * 'trackClicks': boolean, - * 'useDraftTemplate': boolean + * 'useDraftTemplate': boolean * * @return array API repsonse represented as key-value pairs */ - public static function send( $transmissionConfig ) { - return self::sendRequest( $transmissionConfig ); + public function send( $transmissionConfig ) { + return $this->create( $transmissionConfig ); } - + /** * @desc Method for retrieving information about all transmissions * Wrapper method for a cleaner interface - * + * * @return array result Set of transmissions */ - public static function all( $campaignID=null, $templateID=null ) { - $options = array(); + public function all( $campaignID=null, $templateID=null ) { + $options = []; if( $campaignID !== NULL ) $options['campaign_id'] = $campaignID; if( $templateID !== NULL ) $options['template_id'] = $templateID; - - return self::fetchResource( null, $options ); + + return $this->get( null, $options ); } - + /** * @desc Method for retrieving information about a single transmission * Wrapper method for a cleaner interface - * + * * @param string $transmissionID Identifier of the transmission to be found * @return array result Single transmission represented in key-value pairs */ - public static function find($transmissionID) { - return self::fetchResource($transmissionID); - } - - public static function delete( $transmissionID ) { - return self::deleteResource($transmissionID); + public function find($transmissionID) { + return $this->get($transmissionID); } } -?>
\ No newline at end of file +?> |