From 39151786ade97e796533c66e61e312fe4f4f99cc Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 27 Mar 2015 01:10:11 -0700 Subject: [PATCH] CRM-16173 - Cxn API - Add "get". Fix GUID fields and API metadata. APIv3 overloads fields like "cxn_id" as aliases for numeric "id". Therefore, it's problematic to use "cxn_id" and "app_id" as GUID fields. This commit renames them to "cxn_guid" and "app_guid". --- CRM/Cxn/BAO/Cxn.php | 4 +- CRM/Cxn/CiviCxnStore.php | 21 +++---- api/v3/Cxn.php | 117 ++++++++++++++++++++++++++++++++++----- xml/schema/Cxn/Cxn.xml | 8 +-- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/CRM/Cxn/BAO/Cxn.php b/CRM/Cxn/BAO/Cxn.php index 76150455a8..26a57c70c9 100644 --- a/CRM/Cxn/BAO/Cxn.php +++ b/CRM/Cxn/BAO/Cxn.php @@ -65,7 +65,7 @@ class CRM_Cxn_BAO_Cxn extends CRM_Cxn_DAO_Cxn { */ public static function updateAppMeta($appMeta) { \Civi\Cxn\Rpc\AppMeta::validate($appMeta); - CRM_Core_DAO::executeQuery('UPDATE civicrm_cxn SET app_meta = %1 WHERE app_id = %2', array( + CRM_Core_DAO::executeQuery('UPDATE civicrm_cxn SET app_meta = %1 WHERE app_guid = %2', array( 1 => array(json_encode($appMeta), 'String'), 2 => array($appMeta['appId'], 'String'), )); @@ -79,7 +79,7 @@ class CRM_Cxn_BAO_Cxn extends CRM_Cxn_DAO_Cxn { * @throws \Civi\Cxn\Rpc\Exception\CxnException */ public static function getAppMeta($cxnId) { - $appMetaJson = CRM_Core_DAO::getFieldValue('CRM_Cxn_DAO_Cxn', $cxnId, 'app_meta', 'cxn_id', TRUE); + $appMetaJson = CRM_Core_DAO::getFieldValue('CRM_Cxn_DAO_Cxn', $cxnId, 'app_meta', 'cxn_guid', TRUE); $appMeta = json_decode($appMetaJson, TRUE); \Civi\Cxn\Rpc\AppMeta::validate($appMeta); return $appMeta; diff --git a/CRM/Cxn/CiviCxnStore.php b/CRM/Cxn/CiviCxnStore.php index 8503fa24ab..eb05f86119 100644 --- a/CRM/Cxn/CiviCxnStore.php +++ b/CRM/Cxn/CiviCxnStore.php @@ -28,7 +28,7 @@ class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface { return $this->cxns[$cxnId]; } $dao = new CRM_Cxn_DAO_Cxn(); - $dao->cxn_id = $cxnId; + $dao->cxn_guid = $cxnId; if ($dao->find(TRUE)) { $this->cxns[$cxnId] = $this->convertDaoToCxn($dao); return $this->cxns[$cxnId]; @@ -43,10 +43,10 @@ class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface { */ public function getByAppId($appId) { $dao = new CRM_Cxn_DAO_Cxn(); - $dao->app_id = $appId; + $dao->app_guid = $appId; if ($dao->find(TRUE)) { - $this->cxns[$dao->cxn_id] = $this->convertDaoToCxn($dao); - return $this->cxns[$dao->cxn_id]; + $this->cxns[$dao->cxn_guid] = $this->convertDaoToCxn($dao); + return $this->cxns[$dao->cxn_guid]; } else { return NULL; @@ -58,7 +58,7 @@ class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface { */ public function add($cxn) { $dao = new CRM_Cxn_DAO_Cxn(); - $dao->cxn_id = $cxn['cxnId']; + $dao->cxn_guid = $cxn['cxnId']; $dao->find(TRUE); $this->convertCxnToDao($cxn, $dao); $dao->save(); @@ -79,7 +79,7 @@ class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface { * @inheritDoc */ public function remove($cxnId) { - CRM_Core_DAO::executeQuery('DELETE FROM civicrm_cxn WHERE cxn_id = %1', array( + CRM_Core_DAO::executeQuery('DELETE FROM civicrm_cxn WHERE cxn_guid = %1', array( 1 => array($cxnId, 'String'), )); unset($this->cxns[$cxnId]); @@ -93,9 +93,9 @@ class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface { protected function convertDaoToCxn($dao) { $appMeta = json_decode($dao->app_meta, TRUE); return array( - 'cxnId' => $dao->cxn_id, + 'cxnId' => $dao->cxn_guid, 'secret' => $dao->secret, - 'appId' => $dao->app_id, + 'appId' => $dao->app_guid, 'appUrl' => $appMeta['appUrl'], 'siteUrl' => CRM_Cxn_BAO_Cxn::getSiteCallbackUrl(), 'perm' => json_decode($dao->perm, TRUE), @@ -108,12 +108,13 @@ class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface { * @param CRM_Cxn_DAO_Cxn $dao */ protected function convertCxnToDao($cxn, $dao) { - $dao->cxn_id = $cxn['cxnId']; + $dao->cxn_guid = $cxn['cxnId']; $dao->secret = $cxn['secret']; - $dao->app_id = $cxn['appId']; + $dao->app_guid = $cxn['appId']; $dao->perm = json_encode($cxn['perm']); // Note: we don't save siteUrl because it's more correct to regenerate on-demand. // Note: we don't save appUrl, but other processes will update appMeta. } + } diff --git a/api/v3/Cxn.php b/api/v3/Cxn.php index 29c53b3c95..9d24a3ed32 100644 --- a/api/v3/Cxn.php +++ b/api/v3/Cxn.php @@ -26,19 +26,58 @@ */ /** + * The Cxn API allows a Civi site to initiate a connection to a + * remote application. There are three primary actions: + * + * - register: Establish a new connection. + * - unregister: Destroy an existing connection. + * - get: Get a list of existing connections. + */ + +/** + * Adjust metadata for "register" action. + * + * @param array $spec + * List of fields. + */ +function _civicrm_api3_cxn_register_spec(&$spec) { + $daoFields = CRM_Cxn_DAO_Cxn::fields(); + $spec['app_guid'] = $daoFields['app_guid']; + + if (!CRM_Cxn_BAO_Cxn::isAppMetaVerified()) { + $spec['app_meta_url'] = array( + 'name' => 'app_meta_url', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Application Metadata URL'), + 'description' => 'Application Metadata URL', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + ); + } +} + +/** + * Register with a remote application and create a new connection. + * + * One should generally identify an application using the app_guid. + * However, if you need to test a new/experimental application, then + * disable CIVICRM_CXN_CA and specify app_meta_url. + * * @param array $params * Array with keys: - * - appMeta: the application's metadata. + * - app_guid: The unique identifer of the target application. + * - app_meta_url: The URL for the application's metadata. * @return array + * @throws Exception */ function civicrm_api3_cxn_register($params) { - if (empty($params['appMeta']) && !empty($params['appMetaUrl'])) { + if (!empty($params['app_meta_url'])) { if (!CRM_Cxn_BAO_Cxn::isAppMetaVerified()) { - list ($status, $json) = CRM_Utils_HttpClient::singleton()->get($params['appMetaUrl']); + list ($status, $json) = CRM_Utils_HttpClient::singleton()->get($params['app_meta_url']); if (CRM_Utils_HttpClient::STATUS_OK != $status) { throw new API_Exception("Failed to download appMeta."); } - $params['appMeta'] = json_decode($json, TRUE); + $appMeta = json_decode($json, TRUE); } else { // Note: The metadata includes a cert, but the details aren't signed. @@ -47,38 +86,75 @@ function civicrm_api3_cxn_register($params) { throw new API_Exception('This site is configured to only connect to applications with verified metadata.'); } } + elseif (!empty($params['app_guid'])) { + $appMeta = civicrm_api3('CxnApp', 'getsingle', array( + 'app_guid' => $params['app_guid'], + )); + } - if (empty($params['appMeta']) || !is_array($params['appMeta'])) { - throw new API_Exception("Missing expected parameter: appMeta (array)"); + if (empty($appMeta) || !is_array($appMeta)) { + throw new API_Exception("Missing expected parameter: app_guid"); } - \Civi\Cxn\Rpc\AppMeta::validate($params['appMeta']); + \Civi\Cxn\Rpc\AppMeta::validate($appMeta); try { /** @var \Civi\Cxn\Rpc\RegistrationClient $client */ $client = \Civi\Core\Container::singleton()->get('cxn_reg_client'); - list($cxnId, $result) = $client->register($params['appMeta']); - CRM_Cxn_BAO_Cxn::updateAppMeta($params['appMeta']); + list($cxnId, $result) = $client->register($appMeta); + CRM_Cxn_BAO_Cxn::updateAppMeta($appMeta); } catch (Exception $e) { - CRM_Cxn_BAO_Cxn::updateAppMeta($params['appMeta']); + CRM_Cxn_BAO_Cxn::updateAppMeta($appMeta); throw $e; } return $result; } +function _civicrm_api3_cxn_unregister_spec(&$spec) { + $daoFields = CRM_Cxn_DAO_Cxn::fields(); + $spec['cxn_guid'] = $daoFields['cxn_guid']; + $spec['app_guid'] = $daoFields['app_guid']; + $spec['force'] = array( + 'name' => 'force', + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'title' => ts('Force'), + 'description' => 'Destroy connection even if the remote application is non-responsive.', + 'default' => '0', + ); +} + /** + * Unregister with a remote application; destroy an existing connection. + * + * Specify app_guid XOR cxn_guid. + * * @param array $params * Array with keys: - * - cxnId: string + * - cxn_guid: string + * - app_guid: string + * - force: bool * @return array */ function civicrm_api3_cxn_unregister($params) { - if (empty($params['cxnId'])) { - throw new API_Exception('Missing required parameter: cxnId'); + $cxnId = NULL; + + if (!empty($params['cxn_guid'])) { + $cxnId = $params['cxn_guid']; + } + elseif (!empty($params['app_guid'])) { + $cxnId = CRM_Core_DAO::singleValueQuery('SELECT cxn_guid FROM civicrm_cxn WHERE app_guid = %1', array( + 1 => array($params['app_guid'], 'String'), + )); + if (!$cxnId) { + throw new API_Exception("The app_guid does not correspond to an active connection."); + } + } + if (!$cxnId) { + throw new API_Exception('Missing required parameter: cxn_guid'); } - $appMeta = CRM_Cxn_BAO_Cxn::getAppMeta($params['cxnId']); + $appMeta = CRM_Cxn_BAO_Cxn::getAppMeta($cxnId); /** @var \Civi\Cxn\Rpc\RegistrationClient $client */ $client = \Civi\Core\Container::singleton()->get('cxn_reg_client'); @@ -86,3 +162,16 @@ function civicrm_api3_cxn_unregister($params) { return $result; } + +/** + * Returns an array of Cxn records. + * + * @param array $params + * Array of one or more valid property_name=>value pairs. + * + * @return array + * API result array. + */ +function civicrm_api3_cxn_get($params) { + return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); +} diff --git a/xml/schema/Cxn/Cxn.xml b/xml/schema/Cxn/Cxn.xml index 8d945de402..dc06a3dfc2 100644 --- a/xml/schema/Cxn/Cxn.xml +++ b/xml/schema/Cxn/Cxn.xml @@ -18,7 +18,7 @@ - app_id + app_guid Application GUID varchar 128 @@ -27,7 +27,7 @@ UI_appid - app_id + app_guid true 4.6 @@ -42,7 +42,7 @@ - cxn_id + cxn_guid Connection GUID varchar 128 @@ -51,7 +51,7 @@ UI_keypair_cxnid - cxn_id + cxn_guid true 4.6 -- 2.25.1