From 523c222f1c90e39ec67820636d4e1c2ead8a2835 Mon Sep 17 00:00:00 2001 From: eileen Date: Tue, 4 Jul 2017 15:05:15 +1200 Subject: [PATCH] CRM-20847 Support custom api with composite primary keys Do not add id if the field does not exist and add unit tests. This adds a couple of checks in the api to not add the id field Note that Grant is an example of an api which uses a uniqueName on id & I checked manually that is still returned. SyntaxConformance tests cover this a lot, but I still also added one check to the grant test. --- Civi/API/SelectQuery.php | 6 +- api/v3/utils.php | 6 +- tests/phpunit/api/v3/CustomApiTest.php | 114 +++++++ tests/phpunit/api/v3/GrantTest.php | 2 +- .../api/v3/custom_api/MailingProviderData.php | 294 ++++++++++++++++++ 5 files changed, 417 insertions(+), 5 deletions(-) create mode 100644 tests/phpunit/api/v3/CustomApiTest.php create mode 100644 tests/phpunit/api/v3/custom_api/MailingProviderData.php diff --git a/Civi/API/SelectQuery.php b/Civi/API/SelectQuery.php index 7e047bd182..b052ec4189 100644 --- a/Civi/API/SelectQuery.php +++ b/Civi/API/SelectQuery.php @@ -467,8 +467,10 @@ abstract class SelectQuery { } } - // Always select the ID. - $this->selectFields[self::MAIN_TABLE_ALIAS . ".id"] = "id"; + // Always select the ID if the table has one. + if (array_key_exists('id', $this->apiFieldSpec)) { + $this->selectFields[self::MAIN_TABLE_ALIAS . ".id"] = "id"; + } // core return fields foreach ($return as $fieldName) { diff --git a/api/v3/utils.php b/api/v3/utils.php index 7f0c00cc3b..9f4288670b 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -922,8 +922,10 @@ function _civicrm_api3_build_fields_array(&$bao, $unique = TRUE) { if ($unique) { if (empty($fields['id'])) { $lowercase_entity = _civicrm_api_get_entity_name_from_camel(_civicrm_api_get_entity_name_from_dao($bao)); - $fields['id'] = $fields[$lowercase_entity . '_id']; - unset($fields[$lowercase_entity . '_id']); + if (isset($fields[$lowercase_entity . '_id'])) { + $fields['id'] = $fields[$lowercase_entity . '_id']; + unset($fields[$lowercase_entity . '_id']); + } } return $fields; } diff --git a/tests/phpunit/api/v3/CustomApiTest.php b/tests/phpunit/api/v3/CustomApiTest.php new file mode 100644 index 0000000000..715cc2a73d --- /dev/null +++ b/tests/phpunit/api/v3/CustomApiTest.php @@ -0,0 +1,114 @@ +installApi(); + } + + public function tearDown() { + parent::tearDown(); + CRM_Core_DAO::executeQuery('DROP TABLE civicrm_mailing_provider_data'); + } + + /** + * Test that a custom api, as one would add in an extension works. + * + * This api is a bit 'special' in that it has a composite primary key rather + * than using 'id', make sure that works too.... + */ + public function testCustomApi() { + $this->installApi(); + $this->callAPISuccess('MailingProviderData', 'create', array( + 'contact_identifier' => 'xyz', + 'mailing_identifier' => 'abx', + )); + $this->callAPISuccess('Mailing', 'create', array('name' => 'CiviMail', 'hash' => 'abx')); + $result = $this->callAPISuccess('MailingProviderData', 'get', array('return' => array('mailing_identifier.name', 'contact_identifier', 'mailing_identifier'))); + $this->assertEquals(1, $result['count']); + $this->assertEquals('xyzabx0000-00-00 00:00:00', $result['id']); + $this->assertEquals('xyzabx0000-00-00 00:00:00', $result['id']); + $this->assertEquals(array( + 'contact_identifier' => 'xyz', + 'mailing_identifier' => 'abx', + 'mailing_identifier.name' => 'CiviMail', + ), + reset($result['values']) + ); + } + + /** + * * Implements hook_civicrm_EntityTypes(). + * + * @param array $entityTypes + */ + public function hookEntityTypes(&$entityTypes) { + $entityTypes['CRM_Omnimail_DAO_MailingProviderData'] = array( + 'name' => 'MailingProviderData', + 'class' => 'CRM_Omnimail_DAO_MailingProviderData', + 'table' => 'civicrm_maiing_provider_data', + ); + } + + /** + * Install the custom api. + */ + public function installApi() { + require_once __DIR__ . '/custom_api/MailingProviderData.php'; + $this->hookClass->setHook('civicrm_entityTypes', array($this, 'hookEntityTypes')); + CRM_Core_DAO_AllCoreTables::init(TRUE); + CRM_Core_DAO::executeQuery( + "CREATE TABLE IF NOT EXISTS `civicrm_mailing_provider_data` ( + `contact_identifier` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', + `mailing_identifier` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `event_type` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', + `recipient_action_datetime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `contact_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `is_civicrm_updated` TINYINT(4) DEFAULT '0', + PRIMARY KEY (`contact_identifier`,`recipient_action_datetime`,`event_type`), + KEY `contact_identifier` (`contact_identifier`), + KEY `mailing_identifier` (`mailing_identifier`), + KEY `contact_id` (`contact_id`), + KEY `email` (`email`), + KEY `event_type` (`event_type`), + KEY `recipient_action_datetime` (`recipient_action_datetime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci" + ); + } + +} diff --git a/tests/phpunit/api/v3/GrantTest.php b/tests/phpunit/api/v3/GrantTest.php index bae689b4b5..5ccc2aca2d 100644 --- a/tests/phpunit/api/v3/GrantTest.php +++ b/tests/phpunit/api/v3/GrantTest.php @@ -131,7 +131,7 @@ class api_v3_GrantTest extends CiviUnitTestCase { $result = $this->callAPISuccess($this->_entity, 'create', $this->params); $this->ids['grant'][0] = $result['id']; $result = $this->callAPIAndDocument($this->_entity, 'get', array('rationale' => 'Just Because'), __FUNCTION__, __FILE__); - $this->assertAPISuccess($result); + $this->assertEquals($result['id'], $result['values'][$result['id']]['id']); $this->assertEquals(1, $result['count']); } diff --git a/tests/phpunit/api/v3/custom_api/MailingProviderData.php b/tests/phpunit/api/v3/custom_api/MailingProviderData.php new file mode 100644 index 0000000000..f57e8034d8 --- /dev/null +++ b/tests/phpunit/api/v3/custom_api/MailingProviderData.php @@ -0,0 +1,294 @@ +__table = 'civicrm_mailing_provider_data'; + parent::__construct(); + } + /** + * Returns all the column names of this table + * + * @return array + */ + public static function &fields() { + if (!isset(Civi::$statics[__CLASS__]['fields'])) { + Civi::$statics[__CLASS__]['fields'] = array( + 'contact_identifier' => array( + 'name' => 'contact_identifier', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Contact Identifier') , + 'description' => 'External reference for the contact', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + 'mailing_identifier' => array( + 'name' => 'mailing_identifier', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Mailing Identifier') , + 'description' => 'External Referencefor the mailing', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + 'email' => array( + 'name' => 'email', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Email') , + 'description' => 'Email Address', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + 'event_type' => array( + 'name' => 'event_type', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => ts('Recipient Action Type') , + 'description' => 'Type of action', + 'maxlength' => 255, + 'size' => CRM_Utils_Type::HUGE, + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + 'recipient_action_datetime' => array( + 'name' => 'recipient_action_datetime', + 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'title' => ts('Recipient Action Datetime') , + 'description' => 'When the action happened', + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + 'contact_id' => array( + 'name' => 'contact_id', + 'type' => CRM_Utils_Type::T_INT, + 'description' => 'Contact in CiviCRM', + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + 'is_civicrm_updated' => array( + 'name' => 'is_civicrm_updated', + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'description' => 'Has the action been synchronised through to CiviCRM', + 'table_name' => 'civicrm_mailing_provider_data', + 'entity' => 'MailingProviderData', + 'bao' => 'CRM_Omnimail_DAO_MailingProviderData', + 'localizable' => 0, + ) , + ); + CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); + } + return Civi::$statics[__CLASS__]['fields']; + } + /** + * Return a mapping from field-name to the corresponding key (as used in fields()). + * + * @return array + * Array(string $name => string $uniqueName). + */ + public static function &fieldKeys() { + if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { + Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); + } + return Civi::$statics[__CLASS__]['fieldKeys']; + } + /** + * Returns the names of this table + * + * @return string + */ + public static function getTableName() { + return self::$_tableName; + } + /** + * Returns if this table needs to be logged + * + * @return bool + */ + public function getLog() { + return self::$_log; + } + /** + * Returns the list of fields that can be imported + * + * @param bool $prefix + * + * @return array + */ + public static function &import($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'mailing_provider_data', $prefix, array()); + return $r; + } + /** + * Returns the list of fields that can be exported + * + * @param bool $prefix + * + * @return array + */ + public static function &export($prefix = FALSE) { + $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'mailing_provider_data', $prefix, array()); + return $r; + } + /** + * Returns the list of indices + */ + public static function indices($localize = TRUE) { + $indices = array(); + return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; + } + +} + +/** + * CRM_Omnimail_BAO_MailingProviderData constructor. + */ +class CRM_Omnimail_BAO_MailingProviderData extends CRM_Omnimail_DAO_MailingProviderData { + +} + +/** + * MailingProviderData.get API + * + * @param array $params + * @return array API result descriptor + * @throws API_Exception + */ +function civicrm_api3_mailing_provider_data_get($params) { + $sql = CRM_Utils_SQL_Select::fragment(); + $sql->select('CONCAT(contact_identifier, mailing_identifier, recipient_action_datetime) as id'); + return civicrm_api3_create_success(_civicrm_api3_basic_get('CRM_Omnimail_BAO_MailingProviderData', $params, FALSE, 'MailingProviderData', $sql, FALSE)); +} + +/** + * Metadata for MailingProviderData.get API + * + * @param array $params + * + * @throws API_Exception + */ +function _civicrm_api3_mailing_provider_data_get_spec(&$params) { + $params['mailing_identifier']['FKClassName'] = 'CRM_Mailing_BAO_Mailing'; + $params['mailing_identifier']['FKApiName'] = 'Mailing'; + $params['mailing_identifier']['FKKeyColumn'] = 'hash'; +} + +/** + * MailingProviderData.create API + * + * @param array $params + * @return array API result descriptor + * @throws API_Exception + */ +function civicrm_api3_mailing_provider_data_create($params) { + return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params); +} -- 2.25.1