From 0e4d43988abb5b5efef913cdcb75a1dd8986b988 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 7 Aug 2013 21:08:09 -0700 Subject: [PATCH] CRM-13163 - APIv3 (*.create) - Add support for options.reload=1 This will refetch the created record with the API to ensure that the return value is really correct. ---------------------------------------- * CRM-13163: hrjob: Display/edit dates with jQuery date picker http://issues.civicrm.org/jira/browse/CRM-13163 --- CRM/Utils/API/ReloadOption.php | 113 ++++++++++++++++++ api/api.php | 3 +- .../CRM/Utils/API/ReloadOptionTest.php | 107 +++++++++++++++++ 3 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 CRM/Utils/API/ReloadOption.php create mode 100644 tests/phpunit/CRM/Utils/API/ReloadOptionTest.php diff --git a/CRM/Utils/API/ReloadOption.php b/CRM/Utils/API/ReloadOption.php new file mode 100644 index 0000000000..45677c6fb8 --- /dev/null +++ b/CRM/Utils/API/ReloadOption.php @@ -0,0 +1,113 @@ + array( + * 'reload' => 1 + * ), + * )); + * @endcode + * + * @package CRM + * @copyright CiviCRM LLC (c) 2004-2013 + * $Id$ + */ + +require_once 'api/Wrapper.php'; +class CRM_Utils_API_ReloadOption implements API_Wrapper { + + /** + * @var null|'null'|'default'|'selected' + */ + private $reloadMode = NULL; + + /** + * {@inheritDoc} + */ + public function fromApiInput($apiRequest) { + if ($apiRequest['action'] === 'create' && isset($apiRequest['params'], $apiRequest['params']['options'], $apiRequest['params']['options']['reload'])) { + $this->reloadMode = $apiRequest['params']['options']['reload']; + } + return $apiRequest; + } + + /** + * {@inheritDoc} + */ + public function toApiOutput($apiRequest, $result) { + if ($result['is_error']) { + return $result; + } + switch ($this->reloadMode) { + case NULL: + case '0': + case 'null': + return $result; + + case '1': + case 'default': + $params = array( + 'id' => $result['id'], + ); + $reloadResult = civicrm_api3($apiRequest['entity'], 'get', $params); + $result['values'][$result['id']] = array_merge($result['values'][$result['id']], $reloadResult['values'][$result['id']]); + return $result; + + case 'selected': + $params = array( + 'id' => $result['id'], + 'return' => $this->pickReturnFields($apiRequest), + ); + $reloadResult = civicrm_api3($apiRequest['entity'], 'get', $params); + $result['values'][$result['id']] = array_merge($result['values'][$result['id']], $reloadResult['values'][$result['id']]); + return $result; + + default: + throw new API_Exception("Unknown reload mode: " . var_export($this->reloadMode, TRUE)); + } + } + + /** + * Identify the fields which should be returned + * + * @param $apiRequest + * @return array + */ + public function pickReturnFields($apiRequest) { + $fields = civicrm_api3($apiRequest['entity'], 'getfields', array()); + $returnKeys = array_intersect( + array_keys($apiRequest['params']), + array_keys($fields['values']) + ); + return $returnKeys; + } +} diff --git a/api/api.php b/api/api.php index 692b54d0e8..d339644e98 100644 --- a/api/api.php +++ b/api/api.php @@ -21,7 +21,8 @@ function civicrm_api($entity, $action, $params, $extra = NULL) { $apiWrappers = array( CRM_Utils_API_HTMLInputCoder::singleton(), - CRM_Utils_API_NullOutputCoder::singleton() + CRM_Utils_API_NullOutputCoder::singleton(), + new CRM_Utils_API_ReloadOption(), ); try { require_once ('api/v3/utils.php'); diff --git a/tests/phpunit/CRM/Utils/API/ReloadOptionTest.php b/tests/phpunit/CRM/Utils/API/ReloadOptionTest.php new file mode 100644 index 0000000000..b7bd307ecc --- /dev/null +++ b/tests/phpunit/CRM/Utils/API/ReloadOptionTest.php @@ -0,0 +1,107 @@ +setHook('civicrm_post', array($this, 'onPost')); + } + + /** + * If reload option is missing, then 'create' returns the inputted nick_name -- despite the + * fact that the hook manipulated the actual DB content. + */ + function testNoReload() { + $result = $this->callAPISuccess('contact', 'create', array( + 'contact_type' => 'Individual', + 'first_name' => 'First', + 'last_name' => 'Last', + 'nick_name' => 'Firstie', + )); + $this->assertEquals('First', $result['values'][$result['id']]['first_name']); + $this->assertEquals('Firstie', $result['values'][$result['id']]['nick_name']); // munged by hook, but we haven't realized it + } + + /** + * When the reload option is unrecognized, generate an error + */ + function testReloadInvalid() { + $this->callAPIFailure('contact', 'create', array( + 'contact_type' => 'Individual', + 'first_name' => 'First', + 'last_name' => 'Last', + 'nick_name' => 'Firstie', + 'options' => array( + 'reload' => 'invalid', + ), + )); + } + + /** + * If reload option is set, then 'create' returns the final nick_name -- even if it + * differs from the inputted nick_name. + */ + function testReloadDefault() { + $result = $this->callAPISuccess('contact', 'create', array( + 'contact_type' => 'Individual', + 'first_name' => 'First', + 'last_name' => 'Last', + 'nick_name' => 'Firstie', + 'options' => array( + 'reload' => 1 + ), + )); + $this->assertEquals('First', $result['values'][$result['id']]['first_name']); + $this->assertEquals('munged', $result['values'][$result['id']]['nick_name']); + } + + /** + * When the reload option is combined with chaining, the reload should munge + * the chain results. + */ + function testReloadNoChainInterference() { + $result = $this->callAPISuccess('contact', 'create', array( + 'contact_type' => 'Individual', + 'first_name' => 'First', + 'last_name' => 'Last', + 'nick_name' => 'Firstie', + 'api.Email.create' => array( + 'email' => 'test@example.com', + ), + 'options' => array( + 'reload' => 1 + ), + )); + $this->assertEquals('First', $result['values'][$result['id']]['first_name']); + $this->assertEquals('munged', $result['values'][$result['id']]['nick_name']); + $this->assertAPISuccess($result['values'][$result['id']]['api.Email.create']); + } + + /** + * Implementation of hook_civicrm_post used with all our test cases + * + * @param $op + * @param $objectName + * @param $objectId + * @param $objectRef + */ + function onPost($op, $objectName, $objectId, &$objectRef) { + if ($op == 'create' && $objectName == 'Individual') { + CRM_Core_DAO::executeQuery( + "UPDATE civicrm_contact SET nick_name = 'munged' WHERE id = %1", + array( + 1 => array($objectId, 'Integer') + ) + ); + } + } +} -- 2.25.1