Skip to content

Instantly share code, notes, and snippets.

@tjlytle
Created January 8, 2012 05:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tjlytle/1577285 to your computer and use it in GitHub Desktop.
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
<?php
//example using as an auth adapter
$cloudmine->setUsername('username@example.com');
$cloudmine->setPassword('password');
$result = Zend_Auth::getInstance()->authenticate($cloudmine);
echo $result->getIdentity(); //username@example.com
<?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