diff --git a/documentation/api/Api.markdown b/documentation/api/Api.markdown index b20eae0c..31b4d6d1 100644 --- a/documentation/api/Api.markdown +++ b/documentation/api/Api.markdown @@ -52,22 +52,10 @@ Every API endpoint returns a JSON response in a [standard envelope][Envelope]. 1. [POST /tag/:id/update.json][PostTag] Modify meta data for a user's tag. -#### Webhook endpoints -1. [GET /webhook/list.json][GetWebhooks] - An internal private API to list the user's webhooks. -1. [GET /webhook/:id/view.json][GetWebhook] - Get a user's webhook by id. -1. [POST /webhook/create.json][PostWebhookCreate] - Create a new webhook for the user. -1. [POST /webhook/:id/delete.json][PostWebHookDelete] - Delete an existing webhook. -1. [POST /webhook/:id/update.json][PostWebHookUpdate] - Update an eixsting webhook. - #### Group endpoints 1. [GET /group/:id/view.json][GetGroup] Get a group. -1. [GET /group/list.json][GetGroups] +1. [GET /groups/list.json][GetGroups] Get a listing of a user's groups. 1. [POST /group/create.json][PostGroupCreate] Create a group. @@ -77,10 +65,10 @@ Every API endpoint returns a JSON response in a [standard envelope][Envelope]. Update a group. #### Webhook endpoints -1. [GET /webhook/list.json][GetWebhooks] - An internal private API to list the user's webhooks. 1. [GET /webhook/:id/view.json][GetWebhook] Get a user's webhook by id. +1. [GET /webhooks/list.json][GetWebhooks] + An internal private API to list the user's webhooks. 1. [POST /webhook/create.json][PostWebhookCreate] Create a new webhook for the user. 1. [POST /webhook/:id/delete.json][PostWebHookDelete] diff --git a/src/libraries/adapters/Database.php b/src/libraries/adapters/Database.php index c3218d8b..caf2ab1f 100644 --- a/src/libraries/adapters/Database.php +++ b/src/libraries/adapters/Database.php @@ -8,11 +8,14 @@ interface DatabaseInterface { public function __construct(); + // + public function errors(); // delete methods can delete or toggle status public function deleteAction($id); public function deleteCredential($id); public function deleteGroup($id); public function deletePhoto($id); + public function deleteWebhook($id); // get methods read public function getCredential($id); public function getCredentials(); @@ -25,6 +28,8 @@ interface DatabaseInterface public function getUser(); public function getTag($tag); public function getTags($filter = array()); + public function getWebhook($id); + public function getWebhooks(); // post methods update public function postCredential($id, $params); public function postGroup($id, $params); @@ -33,6 +38,7 @@ interface DatabaseInterface public function postTag($id, $params); public function postTags($params); public function postTagsCounter($params); + public function postWebhook($id, $params); // put methods create but do not update public function putGroup($id, $params); public function putAction($id, $params); @@ -40,6 +46,8 @@ interface DatabaseInterface public function putPhoto($id, $params); public function putUser($id, $params); public function putTag($id, $params); + public function putWebhook($id, $params); + // general methods public function initialize(); } diff --git a/src/libraries/adapters/DatabaseMySql.php b/src/libraries/adapters/DatabaseMySql.php index 1e7d3726..8485d3c7 100644 --- a/src/libraries/adapters/DatabaseMySql.php +++ b/src/libraries/adapters/DatabaseMySql.php @@ -12,7 +12,7 @@ class DatabaseMySql implements DatabaseInterface * Member variables holding the names to the SimpleDb domains needed and the database object itself. * @access private */ - private $mySqlDb, $mySqlHost, $mySqlUser, $mySqlPassword, $mySqlTablePrefix; + private $errors = array(), $mySqlDb, $mySqlHost, $mySqlUser, $mySqlPassword, $mySqlTablePrefix; /** * Constructor @@ -76,6 +76,29 @@ class DatabaseMySql implements DatabaseInterface return ($res == 1); } + /** + * Delete a webhook from the database + * + * @param string $id ID of the webhook to delete + * @return boolean + */ + public function deleteWebhook($id) + { + // Gh-193 + return false; + } + + /** + * Get a list of errors + * + * @return array + */ + public function errors() + { + // Gh-193 + return $this->errors; + } + /** * Retrieve a credential with $id * @@ -226,6 +249,29 @@ class DatabaseMySql implements DatabaseInterface return $photos; } + /** + * Get a webhook specified by $id + * + * @param string $id ID of the webhook to retrieve + * @return mixed Array on success, FALSE on failure + */ + public function getWebhook($id) + { + // See Gh-193 + return false; + } + + /** + * Get all webhooks for a user + * + * @return mixed Array on success, FALSE on failure + */ + public function getWebhooks() + { + // See Gh-193 + return false; + } + private function buildQuery($filters, $limit, $offset) { // TODO: support logic for multiple conditions @@ -488,6 +534,19 @@ class DatabaseMySql implements DatabaseInterface return $res = 1; } + /** + * Update the information for the webhook record. + * + * @param string $id ID of the webhook to update which is always 1. + * @param array $params Attributes to update. + * @return boolean + */ + public function postWebhook($id, $params) + { + // See Gh-193 + return false; + } + /** * Add a new action to the database * This method does not overwrite existing values present in $params - hence "new action". @@ -579,6 +638,19 @@ class DatabaseMySql implements DatabaseInterface return ($result != -1); } + /** + * Add a new webhook to the database + * + * @param string $id ID of the webhook to update which is always 1. + * @param array $params Attributes to update. + * @return boolean + */ + public function putWebhook($id, $params) + { + // See Gh-193 + return false; + } + /** * Initialize the database by creating the database and tables needed. * This is called from the Setup controller. diff --git a/src/libraries/adapters/DatabaseSimpleDb.php b/src/libraries/adapters/DatabaseSimpleDb.php index b1f5ee4c..2a7bd19a 100644 --- a/src/libraries/adapters/DatabaseSimpleDb.php +++ b/src/libraries/adapters/DatabaseSimpleDb.php @@ -11,7 +11,8 @@ class DatabaseSimpleDb implements DatabaseInterface * Member variables holding the names to the SimpleDb domains needed and the database object itself. * @access private */ - private $db, $domainAction, $domainCredential, $domainPhoto, $domainTag, $domainUser; + private $db, $domainAction, $domainCredential, $domainPhoto, + $domainTag, $domainUser, $domainWebhook, $errors = array(); /** * Constructor @@ -27,6 +28,7 @@ class DatabaseSimpleDb implements DatabaseInterface $this->domainGroup = getConfig()->get('aws')->simpleDbDomain.'Group'; $this->domainUser = getConfig()->get('aws')->simpleDbDomain.'User'; $this->domainTag = getConfig()->get('aws')->simpleDbDomain.'Tag'; + $this->domainWebhook = getConfig()->get('aws')->simpleDbDomain.'Webhook'; } /** @@ -47,6 +49,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function deleteAction($id) { $res = $this->db->delete_attributes($this->domainAction, $id); + $this->logErrors($res); return $res->isOK(); } @@ -58,6 +61,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function deleteCredential($id) { $res = $this->db->delete_attributes($this->domainCredential, $id); + $this->logErrors($res); return $res->isOK(); } @@ -70,6 +74,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function deleteGroup($id) { $res = $this->db->delete_attributes($this->domainGroup, $id); + $this->logErrors($res); return $res->isOK(); } @@ -82,9 +87,33 @@ class DatabaseSimpleDb implements DatabaseInterface public function deletePhoto($id) { $res = $this->db->delete_attributes($this->domainPhoto, $id); + $this->logErrors($res); return $res->isOK(); } + /** + * Delete a webhook from the database + * + * @param string $id ID of the webhook to delete + * @return boolean + */ + public function deleteWebhook($id) + { + $res = $this->db->delete_attributes($this->domainWebhook, $id); + $this->logErrors($res); + return $res->isOK(); + } + + /** + * Get a list of errors + * + * @return array + */ + public function errors() + { + return $this->errors; + } + /** * Retrieve a credential with $id * @@ -94,6 +123,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getCredential($id) { $res = $this->db->select("SELECT * FROM `{$this->domainCredential}` WHERE itemName()='{$id}' AND status='1'", array('ConsistentRead' => 'true')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) return self::normalizeCredential($res->body->SelectResult->Item); else @@ -108,6 +138,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getCredentials() { $res = $this->db->select("SELECT * FROM `{$this->domainCredential}` WHERE status='1'", array('ConsistentRead' => 'true')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) { $credentials = array(); @@ -130,6 +161,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getGroup($id = null) { $res = $this->db->select("SELECT * FROM `{$this->domainGroup}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) return self::normalizeGroup($res->body->SelectResult->Item); else @@ -149,6 +181,7 @@ class DatabaseSimpleDb implements DatabaseInterface else $res = $this->db->select("SELECT * FROM `{$this->domainGroup}` WHERE members in ('{$email}') AND `name` IS NOT NULL ORDER BY `name`", array('ConsistentRead' => 'true')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) { $groups = array(); @@ -171,6 +204,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getPhoto($id) { $res = $this->db->select("SELECT * FROM `{$this->domainPhoto}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) return self::normalizePhoto($res->body->SelectResult->Item); else @@ -194,6 +228,7 @@ class DatabaseSimpleDb implements DatabaseInterface $this->db->batch($queue)->select("SELECT * FROM `{$this->domainPhoto}` {$buildQuery['where']} AND dateTaken>'{$photo['dateTaken']}' ORDER BY dateTaken ASC LIMIT 1"); $this->db->batch($queue)->select("SELECT * FROM `{$this->domainPhoto}` {$buildQuery['where']} AND dateTaken<'{$photo['dateTaken']}' ORDER BY dateTaken DESC LIMIT 1"); $responses = $this->db->batch($queue)->send(); + $this->logErrors($responses); if(!$responses->areOK()) return false; @@ -219,6 +254,7 @@ class DatabaseSimpleDb implements DatabaseInterface $this->db->batch($queue)->select("SELECT * FROM `{$this->domainPhoto}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true')); $this->db->batch($queue)->select("SELECT * FROM `{$this->domainAction}` WHERE targetType='photo' AND targetId='{$id}'", array('ConsistentRead' => 'true')); $responses = $this->db->batch($queue)->send(); + $this->logErrors($responses); if(!$responses->areOk()) return false; @@ -237,6 +273,8 @@ class DatabaseSimpleDb implements DatabaseInterface * Get a list of a user's photos filtered by $filter, $limit and $offset * * @param array $filters Filters to be applied before obtaining the result + * @param int $limit Total results to return + * @param offset $offset Starting point of results to return * @return mixed Array on success, FALSE on failure */ public function getPhotos($filters = array(), $limit, $offset = null) @@ -249,6 +287,7 @@ class DatabaseSimpleDb implements DatabaseInterface $this->db->batch($queue)->select("SELECT COUNT(*) FROM `{$this->domainPhoto}` {$buildQuery['where']}", $buildQuery['params']); $responses = $this->db->batch($queue)->send(); + $this->logErrors($responses); if(!$responses->areOK()) return false; @@ -272,6 +311,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getTag($tag) { $res = $this->db->select("SELECT * FROM `{$this->domainTag}` WHERE itemName()='{$tag}')", array('ConsistentRead' => 'false')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) return self::normalizeTag($res->body->SelectResult->Item); @@ -288,6 +328,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getTags($filters = array()) { $res = $this->db->select("SELECT * FROM `{$this->domainTag}` WHERE `count` IS NOT NULL AND `count` > '0' AND itemName() IS NOT NULL ORDER BY itemName()", array('ConsistentRead' => 'false')); + $this->logErrors($res); $tags = array(); if(isset($res->body->SelectResult)) { @@ -312,6 +353,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function getUser() { $res = $this->db->select("SELECT * FROM `{$this->domainUser}` WHERE itemName()='1'", array('ConsistentRead' => 'true')); + $this->logErrors($res); if(isset($res->body->SelectResult->Item)) return self::normalizeUser($res->body->SelectResult->Item); elseif(isset($res->body->SelectResult)) @@ -320,6 +362,52 @@ class DatabaseSimpleDb implements DatabaseInterface return false; } + /** + * Get a webhook specified by $id + * + * @param string $id ID of the webhook to retrieve + * @return mixed Array on success, FALSE on failure + */ + public function getWebhook($id) + { + $res = $this->db->select("SELECT * FROM `{$this->domainWebhook}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true')); + $this->logErrors($res); + if(isset($res->body->SelectResult->Item)) + return self::normalizeWebhook($res->body->SelectResult->Item); + else + return false; + } + + /** + * Get all webhooks for a user + * + * @return mixed Array on success, FALSE on failure + */ + public function getWebhooks() + { + $res = $this->db->select("SELECT * FROM `{$this->domainWebhook}`", array('ConsistentRead' => 'true')); + + $this->logErrors($res); + if(!$res->isOK()) + return false; + + + if(isset($res->body->SelectResult)) + { + if(isset($res->body->SelectResult->Item)) + { + $webhooks = array(); + foreach($res->body->SelectResult->Item as $webhook) + $webhooks[] = $this->normalizeWebhook($webhook); + + return $webhooks; + } + + return null; + } + return false; + } + /** * Initialize the database by creating the domains needed. * This is called from the Setup controller. @@ -328,23 +416,26 @@ class DatabaseSimpleDb implements DatabaseInterface */ public function initialize() { - $domains = $this->db->get_domain_list("/^{$this->domainPhoto}(Action|Credential|Group|Tag|User)?$/"); - if(count($domains) == 6) + $domains = $this->db->get_domain_list("/^{$this->domainPhoto}(Action|Credential|Group|Tag|User|Webhook)?$/"); + if(count($domains) == 7) return true; + $domainsToCreate = array($this->domainAction, $this->domainCredential, $this->domainGroup, + $this->domainPhoto, $this->domainTag, $this->domainUser, $this->domainWebhook); + $queue = new CFBatchRequest(); - $this->db->batch($queue)->create_domain($this->domainAction); - $this->db->batch($queue)->create_domain($this->domainCredential); - $this->db->batch($queue)->create_domain($this->domainGroup); - $this->db->batch($queue)->create_domain($this->domainPhoto); - $this->db->batch($queue)->create_domain($this->domainTag); - $this->db->batch($queue)->create_domain($this->domainUser); - $responses = $this->db->batch($queue)->send(); - if(!$responses->areOK()) + foreach($domainsToCreate as $domainToCreate) { - foreach($responses as $response) - getLogger()->crit(sprintf('Could not created SimpleDb domains. Repsonse: %s', var_export($response, 1))); + if(!in_array($domainToCreate, $domains)) + { + $this->db->batch($queue)->create_domain($domainToCreate); + getLogger()->info(sprintf('Queueing request to create domain: %s', $domainToCreate)); + } } + + $responses = $this->db->batch($queue)->send(); + getLogger()->info(sprintf('Attempting to create %d domains.', count($responses))); + $this->logErrors($responses); return $responses->areOK(); } @@ -372,6 +463,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function postCredential($id, $params) { $res = $this->db->put_attributes($this->domainCredential, $id, $params, true); + $this->logErrors($res); return $res->isOK(); } @@ -386,6 +478,7 @@ class DatabaseSimpleDb implements DatabaseInterface { $params = self::prepareGroup($id, $params); $res = $this->db->put_attributes($this->domainGroup, $id, $params, true); + $this->logErrors($res); return $res->isOK(); } @@ -401,6 +494,7 @@ class DatabaseSimpleDb implements DatabaseInterface { $params = self::preparePhoto($id, $params); $res = $this->db->put_attributes($this->domainPhoto, $id, $params, true); + $this->logErrors($res); return $res->isOK(); } @@ -415,6 +509,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function postTag($id, $params) { $res = $this->db->put_attributes($this->domainTag, $id, $params, true); + $this->logErrors($res); return $res->isOK(); } @@ -444,6 +539,7 @@ class DatabaseSimpleDb implements DatabaseInterface $this->db->batch($queue)->put_attributes($this->domainTag, $tag, $tagObj, true); } $responses = $this->db->batch($queue)->send(); + $this->logErrors($responses); return $responses->areOK(); } @@ -465,6 +561,7 @@ class DatabaseSimpleDb implements DatabaseInterface // TODO call getTags instead $res = $this->db->select($sql = "SELECT * FROM `{$this->domainTag}` WHERE itemName() IN ('" . implode("','", $justTags) . "')"); + $this->logErrors($res); if(isset($res->body->SelectResult)) { if(isset($res->body->SelectResult->Item)) @@ -491,7 +588,6 @@ class DatabaseSimpleDb implements DatabaseInterface return $this->postTags($updatedTags); } - /** * Update the information for the user record. * This method overwrites existing values present in $params. @@ -504,6 +600,22 @@ class DatabaseSimpleDb implements DatabaseInterface { // make sure we don't overwrite an existing user record $res = $this->db->put_attributes($this->domainUser, $id, $params, true); + $this->logErrors($res); + return $res->isOK(); + } + + /** + * Update the information for the webhook record. + * + * @param string $id ID of the webhook to update which is always 1. + * @param array $params Attributes to update. + * @return boolean + */ + public function postWebhook($id, $params) + { + // make sure we don't overwrite an existing user record + $res = $this->db->put_attributes($this->domainWebhook, $id, $params, true); + $this->logErrors($res); return $res->isOK(); } @@ -518,6 +630,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function putAction($id, $params) { $res = $this->db->put_attributes($this->domainAction, $id, $params); + $this->logErrors($res); return $res->isOK(); } @@ -532,6 +645,7 @@ class DatabaseSimpleDb implements DatabaseInterface public function putCredential($id, $params) { $res = $this->db->put_attributes($this->domainCredential, $id, $params); + $this->logErrors($res); return $res->isOK(); } @@ -555,6 +669,7 @@ class DatabaseSimpleDb implements DatabaseInterface { $params = self::preparePhoto($id, $params); $res = $this->db->put_attributes($this->domainPhoto, $id, $params); + $this->logErrors($res); return $res->isOK(); } @@ -585,11 +700,31 @@ class DatabaseSimpleDb implements DatabaseInterface */ public function putUser($id, $params) { - // make sure we don't overwrite an existing user record $res = $this->db->put_attributes($this->domainUser, $id, $params); + $this->logErrors($res); return $res->isOK(); } + /** + * Add a new webhook to the database + * + * @param string $id ID of the webhook to update which is always 1. + * @param array $params Attributes to update. + * @return boolean + */ + public function putWebhook($id, $params) + { + return $this->postWebhook($id, $params); + } + + /** + * Query builder for searching photos. + * + * @param array $filters Filters to be applied before obtaining the result + * @param int $limit Total results to return + * @param offset $offset Starting point of results to return + * @return array Components required to build the query + */ private function buildQuery($filters, $limit, $offset) { // TODO: support logic for multiple conditions @@ -671,6 +806,26 @@ class DatabaseSimpleDb implements DatabaseInterface return "{$existing} AND {$add} "; } + private function logErrors($res) + { + if($res instanceof CFArray) + { + foreach($res as $r) + $this->logErrors($r); + } + else + { + if(!$res->isOK()) + { + foreach($res->body->Errors as $error) + { + $message = $this->errors[] = sprintf('Amazon Web Services error (code %s): %s', $error->Error->Code, $error->Error->Message); + getLogger()->crit($message); + } + } + } + } + /** * Normalizes data from simpleDb into schema definition * @@ -804,6 +959,25 @@ class DatabaseSimpleDb implements DatabaseInterface return $user; } + /** + * Normalizes data from simpleDb into schema definition + * + * @param SimpleXMLObject $raw A user from SimpleDb in SimpleXML. + * @return array + */ + private function normalizeWebhook($raw) + { + $webhook = array(); + $webhook['id'] = strval($raw->Name); + foreach($raw->Attribute as $item) + { + $name = (string)$item->Name; + $value = (string)$item->Value; + $webhook[$name] = $value; + } + return $webhook; + } + /** * Formats a group to be updated or added to the database. * Primarily to properly format members as an array. diff --git a/src/libraries/controllers/ApiWebhookController.php b/src/libraries/controllers/ApiWebhookController.php new file mode 100644 index 00000000..3d058362 --- /dev/null +++ b/src/libraries/controllers/ApiWebhookController.php @@ -0,0 +1,89 @@ + + */ +class ApiWebhookController extends BaseController +{ + /** + * Create a webhook. + * + * @return string Standard JSON envelope + */ + public static function create() + { + //getAuthentication()->requireAuthentication(); + $params = $_POST; + $id = Webhook::add($params); + if($id) + return self::success("Webhook {$id} created", array_merge(array('id' => $id), $params)); + else + return self::error("Error creating webhook {$id}", false); + } + + /** + * Delete a webhook specified by the ID. + * + * @param string $id ID of the webhook to be deleted. + * @return string Standard JSON envelope + */ + public static function delete($id) + { + //getAuthentication()->requireAuthentication(); + $status = Webhook::delete($id); + if($status) + return self::success('Webhook deleted successfully', $id); + else + return self::error('Webhook deletion failure', false); + } + + /** + * Update a webhook specified by the ID. + * + * @param string $id ID of the webhook to be updated. + * @return string Standard JSON envelope + */ + public static function update($id) + { + //getAuthentication()->requireAuthentication(); + $params = $_POST; + $id = Webhook::update($id, $params); + if($id) + return self::success("Webhook {$id} updated", array_merge(array('id' => $id), $params)); + else + return self::error("Error updating webhook {$id}", false); + } + + /** + * Retrieve a webhook from the remote datasource. + * + * @param string $id ID of the webhook to be viewed. + * @return string Standard JSON envelope + */ + public static function view($id) + { + //getAuthentication()->requireAuthentication(); + $webhook = Webhook::getById($id); + if($webhook) + return self::success("Successfully retrieved webhook ({$id})", $webhook); + else + return self::error("Error getting webhook ({$id})", false); + } + + /** + * Retrieve a list of the user's webhooks from the remote datasource. + * + * @return string Standard JSON envelope + */ + public static function list_() + { + //getAuthentication()->requireAuthentication(); + $webhooks = Webhook::getAll(); + if($webhooks) + return self::success("Successfully retrieved webhooks", $webhooks); + else + return self::error("Error getting webhooks", false); + } +} diff --git a/src/libraries/controllers/SetupController.php b/src/libraries/controllers/SetupController.php index a71f277b..82452319 100644 --- a/src/libraries/controllers/SetupController.php +++ b/src/libraries/controllers/SetupController.php @@ -298,6 +298,8 @@ class SetupController $dbErrors[] = 'We were unable to properly connect to your MySql database server. Please verify that the host, username and password are correct and have proper permissions to create a database.'; else $dbErrors[] = 'An unknown error occurred while setting up your file system. Check your error logsto see if there\'s more information about the error.'; + + $dbErrors = array_merge($dbErrors, $dbObj->errors()); } if($fsErrors === false && $dbErrors === false) diff --git a/src/libraries/dependencies.php b/src/libraries/dependencies.php index ed54e84a..052c8ab8 100644 --- a/src/libraries/dependencies.php +++ b/src/libraries/dependencies.php @@ -23,6 +23,7 @@ require getConfig()->get('paths')->controllers . '/ApiUserController.php'; require getConfig()->get('paths')->controllers . '/UserController.php'; require getConfig()->get('paths')->controllers . '/ApiOAuthController.php'; require getConfig()->get('paths')->controllers . '/OAuthController.php'; +require getConfig()->get('paths')->controllers . '/ApiWebhookController.php'; // libraries require getConfig()->get('paths')->external . '/aws/sdk.class.php'; @@ -44,6 +45,7 @@ require getConfig()->get('paths')->models . '/Group.php'; require getConfig()->get('paths')->models . '/Photo.php'; require getConfig()->get('paths')->models . '/Tag.php'; require getConfig()->get('paths')->models . '/User.php'; +require getConfig()->get('paths')->models . '/Webhook.php'; require getConfig()->get('paths')->models . '/Theme.php'; require getConfig()->get('paths')->models . '/Image.php'; require getConfig()->get('paths')->models . '/ImageImageMagick.php'; diff --git a/src/libraries/external/epi/EpiApi.php b/src/libraries/external/epi/EpiApi.php index 8dac8b10..3243b7b9 100644 --- a/src/libraries/external/epi/EpiApi.php +++ b/src/libraries/external/epi/EpiApi.php @@ -31,7 +31,7 @@ class EpiApi public function invoke($route, $httpMethod = EpiRoute::httpGet, $params = array()) { - $routeDef = getRoute()->getRoute($route, $httpMethod); + $routeDef = $this->getRoute($route, $httpMethod); // this is ugly but required if internal and external calls are to work $tmps = array(); @@ -50,6 +50,45 @@ class EpiApi return $retval; } + /** + * EpiApi::getRoute($route); + * @name getRoute + * @author Jaisen Mathai + * @param string $route + * @method getRoute + * @static method + */ + public function getRoute($route, $httpMethod) + { + foreach($this->regexes as $ind => $regex) + { + if(preg_match($regex, $route, $arguments)) + { + array_shift($arguments); + $def = $this->routes[$ind]; + if($httpMethod != $def['httpMethod']) + { + continue; + } + else if(is_array($def['callback']) && method_exists($def['callback'][0], $def['callback'][1])) + { + if(Epi::getSetting('debug')) + getDebug()->addMessage(__CLASS__, sprintf('Matched %s : %s : %s : %s', $httpMethod, $this->route, json_encode($def['callback']), json_encode($arguments))); + return array('callback' => $def['callback'], 'args' => $arguments, 'postprocess' => true); + } + else if(function_exists($def['callback'])) + { + if(Epi::getSetting('debug')) + getDebug()->addMessage(__CLASS__, sprintf('Matched %s : %s : %s : %s', $httpMethod, $this->route, json_encode($def['callback']), json_encode($arguments))); + return array('callback' => $def['callback'], 'args' => $arguments, 'postprocess' => true); + } + + EpiException::raise(new EpiException('Could not call ' . json_encode($def) . " for route {$regex}")); + } + } + EpiException::raise(new EpiException("Could not find route {$this->route} from {$_SERVER['REQUEST_URI']}")); + } + /** * addRoute('/', 'function', 'GET'); * @name addRoute diff --git a/src/libraries/models/Webhook.php b/src/libraries/models/Webhook.php new file mode 100644 index 00000000..ebe27275 --- /dev/null +++ b/src/libraries/models/Webhook.php @@ -0,0 +1,97 @@ + + */ +class Webhook +{ + /** + * Add an action to a photo/video. + * Accepts a set of params that must include a type and targetType + * + * @param array $params Params describing the action to be added + * @return mixed Action ID on success, false on failure + */ + public static function add($params) + { + if(!isset($params['callback']) || !isset($params['topic'])) + { + getLogger()->info(sprintf('Not all required paramaters were passed in to add(), %s', json_encode($params))); + return false; + } + $id = User::getNextId('webhook'); + if($id === false) + { + getLogger()->crit('Could not fetch next webhook ID'); + return false; + } + + $db = getDb(); + $action = $db->putWebhook($id, $params); + if(!$action) + { + getLogger()->crit("Could not save webhook ID ({$id})"); + return false; + } + + return $id; + } + + /** + * Delete a webhook. + * + * @param string $id ID of the webhook to be deleted. + * @return boolean + */ + public static function delete($id) + { + return getDb()->deleteWebhook($id); + } + + /** + * Get webhook by id. + * + * @param string $id ID of the webhook to be deleted. + * @return array + */ + public static function getById($id) + { + return getDb()->getWebhook($id); + } + + /** + * Get all webhooks. + * + * @return array + */ + public static function getAll() + { + return getDb()->getWebhooks(); + } + + /** + * Update a webhook. + * + * @param string $id ID of the webhook to be updated. + * @return boolean + */ + public static function update($id, $params) + { + return getDb()->postWebhook($id, $params); + } + + /** + * Defines the default attributes for a webhook. + * Used when adding a webhook. + * + * @return array + */ + private static function getDefaultAttributes() + { + return array( + 'appId' => getConfig()->get('application')->appId + ); + } +} diff --git a/src/libraries/routes-api.php b/src/libraries/routes-api.php index a17612b2..77116f6e 100644 --- a/src/libraries/routes-api.php +++ b/src/libraries/routes-api.php @@ -66,3 +66,14 @@ getApi()->post('/group/([a-zA-Z0-9]+)/delete.json', array('ApiGroupController', getApi()->post('/group/([a-zA-Z0-9]+)/update.json', array('ApiGroupController', 'update'), EpiApi::external); getApi()->get('/group/([a-zA-Z0-9]+)/view.json', array('ApiGroupController', 'view'), EpiApi::external); getApi()->get('/groups/list.json', array('ApiGroupController', 'list_'), EpiApi::external); + +/* + * Webhook endpoints follow the same convention. + * Everything in []'s are optional + * /webhook[s]/{action}.json + */ +getApi()->post('/webhook/create.json', array('ApiWebhookController', 'create'), EpiApi::external); +getApi()->post('/webhook/([a-zA-Z0-9]+)/delete.json', array('ApiWebhookController', 'delete'), EpiApi::external); +getApi()->post('/webhook/([a-zA-Z0-9]+)/update.json', array('ApiWebhookController', 'update'), EpiApi::external); +getApi()->get('/webhook/([a-zA-Z0-9]+)/view.json', array('ApiWebhookController', 'view'), EpiApi::external); +getApi()->get('/webhooks/list.json', array('ApiWebhookController', 'list_'), EpiApi::internal);