Created
January 8, 2012 05:00
-
-
Save tjlytle/1577285 to your computer and use it in GitHub Desktop.
API Wrapper for CloudMine that can be used as a Zend_Auth Adapter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//example using as an auth adapter | |
$cloudmine->setUsername('[email protected]'); | |
$cloudmine->setPassword('password'); | |
$result = Zend_Auth::getInstance()->authenticate($cloudmine); | |
echo $result->getIdentity(); //[email protected] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class CloudMine implements Zend_Auth_Adapter_Interface | |
{ | |
const API_BASE = 'https://api.cloudmine.me/v1/app/'; | |
const FORMAT_JSON = 'json'; | |
const FORMAT_BINARY = 'binary'; | |
const FORMAT_EMPTY = null; | |
/** | |
* HTTP Client | |
* @var Zend_Http_Client | |
*/ | |
protected $httpClient; | |
/** | |
* CloudMine Application ID | |
* @var string | |
*/ | |
protected $appid; | |
/** | |
* CloudMine Application Key | |
* @var string | |
*/ | |
protected $key; | |
/** | |
* User/App Namespace Flag | |
* @var bool | |
*/ | |
protected $user = false; | |
/** | |
* Username for Auth | |
* @var string | |
*/ | |
protected $username; | |
/** | |
* Password for Auth | |
* @var string | |
*/ | |
protected $password; | |
public function __construct($appid, $key) | |
{ | |
$this->appid = $appid; | |
$this->key = $key; | |
} | |
/** | |
* Requests will be to user namespace. | |
* @return CloudMine | |
*/ | |
public function user() | |
{ | |
$this->user = true; | |
return $this; | |
} | |
/** | |
* Requests will be to app namespace. | |
* @return CloudMine | |
*/ | |
public function app() | |
{ | |
$this->user = false; | |
return $this; | |
} | |
/** | |
* Get Data that matches optional key(s). | |
* | |
* @param array|string $keys | |
*/ | |
public function get($keys = array(), $function = null) | |
{ | |
if(!empty($keys)){ | |
if(!is_array($keys)){ | |
$keys = array($keys); | |
} | |
$this->getHttpClient()->setParameterGet('keys', implode(',', $keys)); | |
} | |
if($function){ | |
$this->getHttpClient()->setParameterGet('f', $function); | |
} | |
$response = $this->getHttpClient() | |
->setUri($this->getApi('text')) | |
->request(Zend_Http_Client::GET); | |
return $this->checkResponse($response); | |
} | |
/** | |
* Get Data that matches cloudmins query. | |
* | |
* @param string $query | |
*/ | |
public function query($query) | |
{ | |
$response = $this->getHttpClient() | |
->setParameterGet('q', $query) | |
->setUri($this->getApi('search')) | |
->request(Zend_Http_Client::GET); | |
return $this->checkResponse($response); | |
} | |
/** | |
* Set Data for a single key, or multiple keys. | |
* | |
* This overwrites any exsisting data. | |
* | |
* If only one argument is passed, it's assumed to be an array structure | |
* of keys => data. If both paramters are passed, the first is considered | |
* the key, the second the data. | |
* | |
* @param array|string $key | |
* @param array $data | |
*/ | |
public function set($key, $data = null, $function = null) | |
{ | |
if($function){ | |
$this->getHttpClient()->setParameterGet('f', $function); | |
} | |
$response = $this->getHttpClient() | |
->setHeaders('Content-Type', 'application/json') | |
->setRawData($this->formatData($key, $data)) | |
->setUri($this->getApi('text')) | |
->request(Zend_Http_Client::PUT); | |
return $this->checkResponse($response); | |
} | |
/** | |
* Update Data for a single key, or multiple keys. | |
* | |
* This merges with any exsisting data. | |
* | |
* If only one argument is passed, it's assumed to be an array structure | |
* of keys => data. If both paramters are passed, the first is considered | |
* the key, the second the data. | |
* | |
* @param array|string $key | |
* @param array $data | |
*/ | |
public function update($key, $data = null, $function = null) | |
{ | |
if($function){ | |
$this->getHttpClient()->setParameterGet('f', $function); | |
} | |
$response = $this->getHttpClient() | |
->setHeaders('Content-Type', 'application/json') | |
->setRawData($this->formatData($key, $data)) | |
->setUri($this->getApi('text')) | |
->request(Zend_Http_Client::POST); | |
return $this->checkResponse($response); | |
} | |
/** | |
* Delete Data matching optional key(s). | |
* | |
* WARNING: CALLING THIS WITHOUT AN ARGUMENT WILL DELETE ALL THE DATA IN | |
* THE APPLICATION | |
* | |
* @param array|string $keys | |
*/ | |
public function delete($keys = array()) | |
{ | |
if(!empty($keys)){ | |
if(!is_array($keys)){ | |
$keys = array($keys); | |
} | |
$this->getHttpClient()->setParameterGet('keys', implode(',', $keys)); | |
} | |
$response = $this->getHttpClient() | |
->setUri($this->getApi('data')) | |
->request(Zend_Http_Client::DELETE); | |
return $this->checkResponse($response, self::FORMAT_EMPTY); | |
} | |
/** | |
* Get Binary data for a key. | |
* | |
* @param string $key | |
*/ | |
public function getBinary($key) | |
{ | |
$response = $this->getHttpClient() | |
->setUri($this->getApi('binary/' . $key)) | |
->request(Zend_Http_Client::GET); | |
return $this->checkResponse($response, self::FORMAT_BINARY); | |
} | |
/** | |
* Set Binary data for a key. | |
* @param string $key | |
* @param string $data | |
* @param unknown_type $type | |
*/ | |
public function setBinary($key, $data, $type = 'application/octet-stream') | |
{ | |
$response = $this->getHttpClient() | |
->setUri($this->getApi('binary/' . $key)) | |
->setRawData($data) | |
->request(Zend_Http_Client::PUT); | |
return $this->checkResponse($response, self::FORMAT_EMPTY); | |
} | |
/** | |
* Format Data for Set/Update | |
* | |
* Takes a key and optional data, returns expected JSON. | |
* | |
* @param array|string $key | |
* @param array $data | |
* @throws Exception | |
*/ | |
protected function formatData($key, $data = null) | |
{ | |
if(isset($data)){ | |
$data = array($key => $data); | |
} else { | |
if(!is_array($key)){ | |
throw new Exception('First argument must be array if second argument is missing.'); | |
} | |
$data = $key; | |
} | |
return Zend_Json::encode($data); | |
} | |
/** | |
* Check API Response | |
* @param Zend_Http_Response $response | |
* @param string $format the kind of body expected | |
* @throws Exception | |
*/ | |
protected function checkResponse(Zend_Http_Response $response, $format = self::FORMAT_JSON) | |
{ | |
switch($response->getStatus()){ | |
case 201: | |
case 200: | |
break; | |
case 400: | |
throw new Exception('Invalid Query'); | |
case 401: | |
throw new Exception('Invalid API Credentials'); | |
case 404: | |
throw new Exception('Invalid Application ID'); | |
default: | |
throw new Exception('Unsupported API Response: ' . $response->getStatus()); | |
} | |
switch($format){ | |
case self::FORMAT_JSON: | |
$data = Zend_Json::decode($response->getBody()); | |
//cloudmine returns and array of successes and failures | |
$keys = array(); | |
foreach($data['success'] as $key => $value){ | |
$keys[$key] = $value; | |
} | |
foreach($data['errors'] as $key => $value){ | |
switch($value['code']){ | |
case 404: | |
$keys[$key] = null; | |
break; | |
case 415: | |
//TODO: link to binary file | |
$keys[$key] = null; | |
break; | |
default: | |
$keys[$key] = null; | |
break; | |
} | |
} | |
if(isset($data['result'])){ | |
$keys['result'] = $data['result']; | |
} | |
return $keys; | |
case self::FORMAT_BINARY: | |
//TODO: do something intelegent with the content type | |
return $response->getBody(); | |
default: | |
return true; | |
} | |
} | |
/** | |
* Authenticate a User | |
* | |
* Cloudmine will try to create users that don't exsist. We don't do that, | |
* so the auth endpoind may be requested twice (once to see if the user | |
* exists, once to auth). | |
* | |
* @see Zend_Auth_Adapter_Interface::authenticate() | |
* @return Zend_Auth_Result | |
*/ | |
public function authenticate($username = false, $pasword = false) | |
{ | |
//populate username and password if needed | |
if(!$username){ | |
$username = $this->username; | |
} | |
if(!$pasword){ | |
$password = $this->password; | |
} | |
//first try without a password, so a new account isn't setup if the | |
//email address is incorrect | |
$response = $this->getHttpClient() | |
->setAuth($username, '', Zend_Http_Client::AUTH_BASIC) | |
->setUri($this->getApi('user/account')) | |
->request(Zend_Http_Client::POST); | |
//a 401 Unauthorized means the username is valid, for anything else | |
//assume the username does not exsist | |
if($response->getStatus() !== 401){ | |
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, $username); | |
} | |
//check again with the password, to validate the credentials | |
//TODO: hey look, this code is almost the same as a few lines ago | |
$response = $this->getHttpClient() | |
->setAuth($username, $password, Zend_Http_Client::AUTH_BASIC) | |
->setUri($this->getApi('user/account')) | |
->request(Zend_Http_Client::POST); | |
//200 means we're all good | |
if($response->getStatus() !== 200){ | |
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, $username); | |
} | |
return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $username); | |
} | |
/** | |
* Create a User | |
* | |
* @param string $username | |
* @param string $password | |
* @throws Exception | |
* @return CloudMine | |
*/ | |
public function createUser($username, $password) | |
{ | |
//make sure the account doesn't already exsist | |
$result = $this->authenticate($username, $password); | |
if($result->getCode() != Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND){ | |
throw new Exception('Username already exists: ' . $username); | |
} | |
//do an auth request | |
//TODO: yup, same code again - who wrote this stuff? | |
$response = $this->getHttpClient() | |
->setAuth($username, $password, Zend_Http_Client::AUTH_BASIC) | |
->setUri($this->getApi('user/account')) | |
->request(Zend_Http_Client::POST); | |
//expect a 201 Created response | |
if($response->getStatus() != 201){ | |
throw new Exception('Unexpected status from API: ' . $response->getStatus()); | |
} | |
return $this; | |
} | |
/** | |
* Set the username when using Zend_Auth or making user namespace requests. | |
* | |
* @param string $username | |
* @return CloudMine | |
*/ | |
public function setUsername($username) { | |
$this->username = $username; | |
return $this; | |
} | |
/** | |
* Set the password when using Zend_Auth or making user namespace requests. | |
* | |
* @param string $password | |
* @return CloudMine | |
*/ | |
public function setPassword($password) { | |
$this->password = $password; | |
return $this; | |
} | |
/** | |
* Get a URI based on the given path and the API URI format. | |
* | |
* @param string $path | |
* @return string | |
*/ | |
public function getApi($path) | |
{ | |
$parts = array(self::API_BASE, $this->appid); | |
if($this->user){ | |
$parts[] = 'user'; | |
} | |
$parts[] = $path; | |
return implode('/', $parts); | |
} | |
/** | |
* Get the HTTP Client | |
* | |
* @return Zend_Http_Client $httpClient | |
*/ | |
public function getHttpClient() { | |
if(empty($this->httpClient)){ | |
$this->httpClient = new Zend_Http_Client(); | |
} | |
//set user/pass if user namespace request | |
if($this->user){ | |
$this->httpClient->setAuth($this->username, $this->password, Zend_Http_Client::AUTH_BASIC); | |
} else { | |
$this->httpClient->setAuth(false); | |
} | |
//set auth header | |
return $this->httpClient->setHeaders('X-CloudMine-ApiKey', $this->key); | |
} | |
/** | |
* Set the HTTP Client | |
* | |
* @param field_type $httpClient | |
*/ | |
public function setHttpClient($httpClient) { | |
$this->httpClient = $httpClient; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment