2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Affero General Public License version 3 as published by the
8 * Free Software Foundation with the addition of the following permission added
9 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18 * You should have received a copy of the GNU Affero General Public License along with
19 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU Affero General Public License version 3.
30 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by
32 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33 * technical reasons, the Appropriate Legal Notices must display the words
34 * "Powered by SugarCRM".
35 ********************************************************************************/
38 require_once('service/v3/SugarWebServiceUtilv3.php');
39 require_once('tests/service/APIv3Helper.php');
42 class RESTAPI4Test extends Sugar_PHPUnit_Framework_TestCase
45 protected $_admin_user;
46 protected $_lastRawResponse;
48 private static $helperObject;
53 public function setUp()
57 require('include/modules.php');
58 $GLOBALS['beanList'] = $beanList;
59 $GLOBALS['beanFiles'] = $beanFiles;
61 //Reload langauge strings
62 $GLOBALS['app_strings'] = return_application_language($GLOBALS['current_language']);
63 $GLOBALS['app_list_strings'] = return_app_list_strings_language($GLOBALS['current_language']);
64 $GLOBALS['mod_strings'] = return_module_language($GLOBALS['current_language'], 'Accounts');
65 //Create an anonymous user for login purposes/
66 $this->_user = SugarTestUserUtilities::createAnonymousUser();
68 $this->_admin_user = SugarTestUserUtilities::createAnonymousUser();
69 $this->_admin_user->status = 'Active';
70 $this->_admin_user->is_admin = 1;
71 $this->_admin_user->save();
72 $GLOBALS['db']->commit(); // Making sure we commit any changes before continuing
74 $GLOBALS['current_user'] = $this->_user;
76 self::$helperObject = new APIv3Helper();
78 //Disable access to the website field.
79 $this->aclRole = new ACLRole();
80 $this->aclRole->name = "Unit Test";
81 $this->aclRole->save();
82 $GLOBALS['db']->commit(); // Making sure we commit any changes before continuing
84 $this->aclRole->set_relationship('acl_roles_users', array('role_id'=>$this->aclRole->id ,'user_id'=> $this->_user->id), false);
85 $GLOBALS['db']->commit(); // Making sure we commit any changes before continuing
88 public function tearDown()
90 $GLOBALS['db']->query("DELETE FROM acl_roles WHERE id IN ( SELECT role_id FROM acl_roles_users WHERE user_id = '{$GLOBALS['current_user']->id}' )");
91 $GLOBALS['db']->query("DELETE FROM acl_roles_users WHERE user_id = '{$GLOBALS['current_user']->id}'");
93 if(isset($GLOBALS['listViewDefs'])) unset($GLOBALS['listViewDefs']);
94 if(isset($GLOBALS['viewdefs'])) unset($GLOBALS['viewdefs']);
95 unset($GLOBALS['beanList']);
96 unset($GLOBALS['beanFiles']);
97 unset($GLOBALS['app_list_strings']);
98 unset($GLOBALS['app_strings']);
99 unset($GLOBALS['mod_strings']);
100 unset($GLOBALS['current_user']);
101 SugarTestUserUtilities::removeAllCreatedAnonymousUsers();
104 protected function _makeRESTCall($method,$parameters)
106 // specify the REST web service to interact with
107 $url = $GLOBALS['sugar_config']['site_url'].'/service/v4/rest.php';
108 // Open a curl session for making the call
109 $curl = curl_init($url);
110 // set URL and other appropriate options
111 curl_setopt($curl, CURLOPT_URL, $url);
112 curl_setopt($curl, CURLOPT_POST, 1);
113 curl_setopt($curl, CURLOPT_HEADER, 0);
114 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
115 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
116 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0);
117 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
118 // build the request URL
119 $json = json_encode($parameters);
120 $postArgs = "method=$method&input_type=JSON&response_type=JSON&rest_data=$json";
121 curl_setopt($curl, CURLOPT_POSTFIELDS, $postArgs);
122 // Make the REST call, returning the result
123 $response = curl_exec($curl);
124 // Close the connection
127 $this->_lastRawResponse = $response;
129 // Convert the result from JSON format to a PHP array
130 return json_decode($response,true);
133 protected function _returnLastRawResponse()
135 return "Error in web services call. Response was: {$this->_lastRawResponse}";
138 protected function _login($user = null)
140 $GLOBALS['db']->commit(); // Making sure we commit any changes before logging in
142 $user = $this->_user;
143 return $this->_makeRESTCall('login',
147 'user_name' => $user->user_name,
148 'password' => $user->user_hash,
151 'application_name' => 'mobile',
152 'name_value_list' => array(),
157 * Ensure the ability to retrieve a module list of recrods that are favorites.
160 public function testGetModuleFavoriteList()
162 $account = new Account();
163 $account->id = uniqid();
164 $account->new_with_id = TRUE;
165 $account->name = "Test " . $account->id;
168 $result = $this->_login($this->_admin_user); // Logging in just before the REST call as this will also commit any pending DB changes
169 $session = $result['id'];
171 $this->_markBeanAsFavorite($session, "Accounts", $account->id);
173 $whereClause = "accounts.name='{$account->name}'";
174 $module = 'Accounts';
177 $returnFields = array('name');
178 $linkNameFields = "";
182 $result = $this->_makeRESTCall('get_entry_list', array($session, $module, $whereClause, $orderBy,$offset, $returnFields,$linkNameFields, $maxResults, $deleted, $favorites));
184 $this->assertEquals($account->id, $result['entry_list'][0]['id'],'Unable to retrieve account favorite list.');
186 $GLOBALS['db']->query("DELETE FROM accounts WHERE id = '{$account->id}'");
187 $GLOBALS['db']->query("DELETE FROM sugarfavorites WHERE record_id = '{$account->id}'");
191 * Test set entries call with name value list format key=>value.
194 public function testSetEntriesCall()
196 $result = $this->_login();
197 $session = $result['id'];
198 $module = 'Contacts';
202 array('first_name' => 'Unit Test', 'last_name' => $c1_uuid),
203 array('first_name' => 'Unit Test', 'last_name' => $c2_uuid)
205 $results = $this->_makeRESTCall('set_entries',
207 'session' => $session,
209 'name_value_lists' => $contacts,
211 $this->assertTrue(isset($results['ids']) && count($results['ids']) == 2);
213 $actual_results = $this->_makeRESTCall('get_entries',
215 'session' => $session,
217 'ids' => $results['ids'],
218 'select_fields' => array('first_name','last_name')
221 $this->assertTrue(isset($actual_results['entry_list']) && count($actual_results['entry_list']) == 2);
222 $this->assertEquals($actual_results['entry_list'][0]['name_value_list']['last_name']['value'], $c1_uuid);
223 $this->assertEquals($actual_results['entry_list'][1]['name_value_list']['last_name']['value'], $c2_uuid);
228 * Test search by module with favorites flag enabled.
231 public function testSearchByModuleWithFavorites()
233 $account = new Account();
234 $account->id = uniqid();
235 $account->assigned_user_id = $this->_user->id;
236 $account->team_id = 1;
237 $account->new_with_id = TRUE;
238 $account->name = "Unit Test Fav " . $account->id;
242 $account2 = new Account();
243 $account2->id = uniqid();
244 $account2->new_with_id = TRUE;
245 $account2->name = "Unit Test Fav " . $account->id;
246 $account->assigned_user_id = $this->_user->id;
249 $result = $this->_login($this->_admin_user); // Logging in just before the REST call as this will also commit any pending DB changes
250 $session = $result['id'];
252 $this->_markBeanAsFavorite($session, "Accounts", $account->id);
254 $searchModules = array('Accounts');
255 $searchString = "Unit Test Fav ";
259 $results = $this->_makeRESTCall('search_by_module',
261 'session' => $session,
262 'search_string' => $searchString,
263 'modules' => $searchModules,
265 'max_results' => $maxResults,
266 'assigned_user_id' => $this->_user->id,
267 'select_fields' => array(),
268 'unified_search_only' => true,
273 $GLOBALS['db']->query("DELETE FROM accounts WHERE name like 'Unit Test %' ");
274 $GLOBALS['db']->query("DELETE FROM sugarfavorites WHERE record_id = '{$account->id}'");
275 $GLOBALS['db']->query("DELETE FROM sugarfavorites WHERE record_id = '{$account2->id}'");
277 $this->assertTrue( self::$helperObject->findBeanIdFromEntryList($results['entry_list'],$account->id,'Accounts'), "Unable to find {$account->id} id in favorites search.");
278 $this->assertFalse( self::$helperObject->findBeanIdFromEntryList($results['entry_list'],$account2->id,'Accounts'), "Account {$account2->id} id in favorites search should not be there.");
281 * Private helper function to mark a bean as a favorite item.
283 * @param string $session
284 * @param string $moduleName
285 * @param string $recordID
287 private function _markBeanAsFavorite($session, $moduleName, $recordID)
289 $result = $this->_makeRESTCall('set_entry',
291 'session' => $session,
292 'module' => 'SugarFavorites',
293 'name_value_list' => array(
294 array('name' => 'record_id', 'value' => $recordID),
295 array('name' => 'module', 'value' => $moduleName),
302 public function testRelateAccountToTwoContacts()
304 $result = $this->_login();
305 $this->assertTrue(!empty($result['id']) && $result['id'] != -1,$this->_returnLastRawResponse());
306 $session = $result['id'];
308 $result = $this->_makeRESTCall('set_entry',
310 'session' => $session,
311 'module' => 'Accounts',
312 'name_value_list' => array(
313 array('name' => 'name', 'value' => 'New Account'),
314 array('name' => 'description', 'value' => 'This is an account created from a REST web services call'),
319 $this->assertTrue(!empty($result['id']) && $result['id'] != -1,$this->_returnLastRawResponse());
321 $accountId = $result['id'];
323 $result = $this->_makeRESTCall('set_entry',
325 'session' => $session,
326 'module' => 'Contacts',
327 'name_value_list' => array(
328 array('name' => 'last_name', 'value' => 'New Contact 1'),
329 array('name' => 'description', 'value' => 'This is a contact created from a REST web services call'),
334 $this->assertTrue(!empty($result['id']) && $result['id'] != -1,$this->_returnLastRawResponse());
336 $contactId1 = $result['id'];
338 $result = $this->_makeRESTCall('set_entry',
340 'session' => $session,
341 'module' => 'Contacts',
342 'name_value_list' => array(
343 array('name' => 'last_name', 'value' => 'New Contact 2'),
344 array('name' => 'description', 'value' => 'This is a contact created from a REST web services call'),
349 $this->assertTrue(!empty($result['id']) && $result['id'] != -1,$this->_returnLastRawResponse());
351 $contactId2 = $result['id'];
353 // now relate them together
354 $result = $this->_makeRESTCall('set_relationship',
356 'session' => $session,
357 'module' => 'Accounts',
358 'module_id' => $accountId,
359 'link_field_name' => 'contacts',
360 'related_ids' => array($contactId1,$contactId2),
364 $this->assertEquals($result['created'],1,$this->_returnLastRawResponse());
366 // check the relationship
367 $result = $this->_makeRESTCall('get_relationships',
369 'session' => $session,
370 'module' => 'Accounts',
371 'module_id' => $accountId,
372 'link_field_name' => 'contacts',
373 'related_module_query' => '',
374 'related_fields' => array('last_name','description'),
375 'related_module_link_name_to_fields_array' => array(),
380 $returnedValues = array();
381 $returnedValues[] = $result['entry_list'][0]['name_value_list']['last_name']['value'];
382 $returnedValues[] = $result['entry_list'][1]['name_value_list']['last_name']['value'];
384 $GLOBALS['db']->query("DELETE FROM accounts WHERE id= '{$accountId}'");
385 $GLOBALS['db']->query("DELETE FROM contacts WHERE id= '{$contactId1}'");
386 $GLOBALS['db']->query("DELETE FROM contacts WHERE id= '{$contactId2}'");
387 $GLOBALS['db']->query("DELETE FROM accounts_contacts WHERE account_id= '{$accountId}'");
389 $this->assertContains('New Contact 1',$returnedValues,$this->_returnLastRawResponse());
390 $this->assertContains('New Contact 2',$returnedValues,$this->_returnLastRawResponse());
394 * Test SQL injection bug in get_entries
396 public function testGetEntriesProspectFilter()
398 $result = $this->_login();
399 $this->assertTrue(!empty($result['id']) && $result['id'] != -1,$this->_returnLastRawResponse());
400 $session = $result['id'];
402 $result = $this->_makeRESTCall('get_entries',
404 'session' => $session,
405 'module' => 'CampaignProspects',
406 'ids' => array("' UNION SELECT id related_id, 'Users' related_type FROM users WHERE '1'='1")
409 $this->assertNull($result);