Skip to content

Commit ed5ce7c

Browse files
committed
Lib: Add dbidAsParam flag, isolate Query settings
- API_GetAppDTMInfo needed to be executed against /db/main with the dbid passed in as a query string parameter. To achieve this, a new flag in the settings.flags object has been created: dbidAsParam (defaults to false). When dbidAsParam is true, any passed in dbid option will be sent via the query string or xml packet instead of the URL path. - Isolated each QuickBaseQuery settings so that an overwritten setting for a specific query does not affect other queries. - Added parsing of non-xml responses - Added API_GetAppDTMInfo response parsing - Added API_GetRoleInfo response parsing - Added API_GrantedDBs response parsing - Added API_UserRoles response parsing - Added appid testing env variable - Added test for API_AddReplaceDBPage - Added test for API_CreateDatabase (pending) - Added test for API_CreateTable (pending) - Added test for API_FindDBByName - Added test for API_GenAddRecordForm - Added test for API_GenResultsTable - Added test for API_GetAncestorInfo - Added test for API_GetAppDTMInfo - Added test for API_GetDBInfo - Added test for API_GetDBPage - Added test for API_GetDBVar - Added test for API_GetNumRecords - Added test for API_GetRoleInfo - Added test for API_GrantedDBs - Added test for API_ImportFromCSV - Added test for API_PurgeRecords - Added test for API_SetDBVar - Added test for API_UserRoles
1 parent f1bdc79 commit ed5ce7c

21 files changed

+835
-48
lines changed

quickbase.php

Lines changed: 104 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class QuickBase {
3333
'includeRids' => true,
3434
'returnPercentage' => false,
3535
'fmt' => 'structured',
36-
'encoding' => 'UTF-8'
36+
'encoding' => 'UTF-8',
37+
'dbidAsParam' => false
3738
),
3839

3940
'status' => array(
@@ -58,8 +59,6 @@ final public function api($action, $options = array()){
5859
->actionRequest()
5960
->constructPayload()
6061
->transmit()
61-
->processResponse()
62-
->checkForAndHandleError()
6362
->actionResponse();
6463

6564
return $query->response;
@@ -104,10 +103,10 @@ class QuickBaseQuery {
104103
public $response = array();
105104

106105
protected $payload = '';
107-
protected $xmlResponse = array();
108106

109107
public function __construct(&$parent, $action = '', $options = array()){
110108
$this->parent = $parent;
109+
$this->settings = array_replace_recursive(array(), $this->parent->settings);
111110
$this->action = $action;
112111
$this->options = $options;
113112

@@ -131,20 +130,20 @@ final public function actionResponse(){
131130
}
132131

133132
final public function addFlags(){
134-
if(!isset($this->options['msInUTC']) && $this->parent->settings['flags']['msInUTC']){
133+
if(!isset($this->options['msInUTC']) && $this->settings['flags']['msInUTC']){
135134
$this->options['msInUTC'] = 1;
136135
}
137136

138-
if(!isset($this->options['appToken']) && $this->parent->settings['appToken']){
139-
$this->options['appToken'] = $this->parent->settings['appToken'];
137+
if(!isset($this->options['appToken']) && $this->settings['appToken']){
138+
$this->options['appToken'] = $this->settings['appToken'];
140139
}
141140

142-
if(!isset($this->options['ticket']) && $this->parent->settings['ticket']){
143-
$this->options['ticket'] = $this->parent->settings['ticket'];
141+
if(!isset($this->options['ticket']) && $this->settings['ticket']){
142+
$this->options['ticket'] = $this->settings['ticket'];
144143
}
145144

146-
if(!isset($this->options['encoding']) && $this->parent->settings['flags']['encoding']){
147-
$this->options['encoding'] = $this->parent->settings['flags']['encoding'];
145+
if(!isset($this->options['encoding']) && $this->settings['flags']['encoding']){
146+
$this->options['encoding'] = $this->settings['flags']['encoding'];
148147
}
149148

150149
return $this;
@@ -153,7 +152,7 @@ final public function addFlags(){
153152
final public function constructPayload(){
154153
$this->payload = '';
155154

156-
if($this->parent->settings['flags']['useXML']){
155+
if($this->settings['flags']['useXML']){
157156
$xmlDoc = new SimpleXMLElement(implode('', array(
158157
'<?xml version="1.0" encoding="',
159158
$this->options['encoding'],
@@ -174,7 +173,7 @@ final public function constructPayload(){
174173
}
175174

176175
final public function checkForAndHandleError(){
177-
if($this->response['errcode'] != $this->parent->settings['status']['errcode']){
176+
if($this->response['errcode'] != $this->settings['status']['errcode']){
178177
if($this->response['errcode'] == 4 && isset($this->parent->settings['username']) && isset($this->parent->settings['password'])){
179178
try {
180179
$newTicket = $this->parent->api('API_Authenticate', array(
@@ -183,13 +182,12 @@ final public function checkForAndHandleError(){
183182
));
184183

185184
$this->parent->settings['ticket'] = $newTicket['ticket'];
185+
$this->settings['ticket'] = $newTicket['ticket'];
186186
$this->options['ticket'] = $newTicket['ticket'];
187187

188188
return $this
189189
->constructPayload()
190-
->transmit()
191-
->processResponse()
192-
->checkForAndHandleError();
190+
->transmit();
193191
}catch(Exception $newTicketErr){
194192
throw $newTicketErr;
195193
}
@@ -217,35 +215,26 @@ final public function processOptions(){
217215
return $this;
218216
}
219217

220-
final public function processResponse(){
221-
$this->response = array();
222-
223-
$this->xml2Arr($this->xmlResponse, $this->response);
224-
225-
$this->cleanXml2Arr($this->response);
226-
227-
return $this;
228-
}
229-
230218
final public function transmit(){
231219
$ch = curl_init(implode('', array(
232-
$this->parent->settings['useSSL'] ? 'https://' : 'http://',
233-
$this->parent->settings['realm'],
220+
$this->settings['useSSL'] ? 'https://' : 'http://',
221+
$this->settings['realm'],
234222
'.',
235-
$this->parent->settings['domain'],
223+
$this->settings['domain'],
236224
'/db/',
237-
isset($this->options['dbid']) ? $this->options['dbid'] : 'main',
225+
isset($this->options['dbid']) && !$this->settings['flags']['dbidAsParam'] ? $this->options['dbid'] : 'main',
238226
'?act=',
239227
$this->action,
240-
$this->parent->settings['flags']['useXML'] ? '' : $this->payload
228+
$this->settings['flags']['useXML'] ? '' : $this->payload
241229
)));
242230

243-
curl_setopt($ch, CURLOPT_PORT, $this->parent->settings['useSSL'] ? 443 : 80);
231+
curl_setopt($ch, CURLOPT_PORT, $this->settings['useSSL'] ? 443 : 80);
244232
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
245233
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
234+
curl_setopt($ch, CURLOPT_HEADER, true);
246235
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
247236

248-
if($this->parent->settings['flags']['useXML']){
237+
if($this->settings['flags']['useXML']){
249238
curl_setopt($ch, CURLOPT_POST, true);
250239
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
251240
'POST /db/'.(isset($this->options['dbid']) ? $this->options['dbid'] : 'main').' HTTP/1.0',
@@ -264,13 +253,32 @@ final public function transmit(){
264253
$errno = curl_errno($ch);
265254
$error = curl_error($ch);
266255

256+
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
257+
267258
curl_close($ch);
268259

269260
if($response === false){
270261
throw new QuickBaseError($errno, $error);
271262
}
272263

273-
$this->xmlResponse = new SimpleXmlIterator($response);
264+
$headers = substr($response, 0, $headerSize);
265+
$body = substr($response, $headerSize);
266+
267+
self::parseCURLHeaders($headers);
268+
269+
if($headers['Content-Type'] === 'application/xml'){
270+
$this->response = array();
271+
272+
$xml = new SimpleXmlIterator($body);
273+
274+
$this->xml2Arr($xml, $this->response);
275+
276+
$this->cleanXml2Arr($this->response);
277+
278+
$this->checkForAndHandleError();
279+
}else{
280+
$this->response = $body;
281+
}
274282

275283
return $this;
276284
}
@@ -355,6 +363,19 @@ final protected static function cleanXml2Arr(&$arr){
355363
}
356364
}
357365

366+
final protected static function parseCURLHeaders(&$headers){
367+
$newHeaders = array();
368+
$headers = explode("\r\n", $headers);
369+
370+
foreach($headers as $header){
371+
$i = strpos($header, ':');
372+
373+
$newHeaders[substr($header, 0, $i)] = substr($header, $i + 2);
374+
}
375+
376+
$headers = $newHeaders;
377+
}
378+
358379
final protected static function xml2Arr($xml, &$arr){
359380
for($xml->rewind(); $xml->valid(); $xml->next()){
360381
$key = $xml->key();
@@ -408,7 +429,7 @@ class QuickBaseRequest {
408429

409430
final public static function API_Authenticate(&$query){
410431
// API_Authenticate can only happen over SSL
411-
$query->parent->settings['useSSL'] = true;
432+
$query->settings['useSSL'] = true;
412433
}
413434

414435
// final public static function API_ChangeGroupInfo(&$query){ }
@@ -427,16 +448,16 @@ final public static function API_Authenticate(&$query){
427448
// final public static function API_DeleteRecord(&$query){ }
428449

429450
final public static function API_DoQuery(&$query){
430-
if(!isset($query->options['returnPercentage']) && isset($query->parent->settings['flags']['returnPercentage'])){
431-
$query->options['returnPercentage'] = $query->parent->settings['flags']['returnPercentage'];
451+
if(!isset($query->options['returnPercentage']) && isset($query->settings['flags']['returnPercentage'])){
452+
$query->options['returnPercentage'] = $query->settings['flags']['returnPercentage'];
432453
}
433454

434-
if(!isset($query->options['fmt']) && isset($query->parent->settings['flags']['fmt'])){
435-
$query->options['fmt'] = $query->parent->settings['flags']['fmt'];
455+
if(!isset($query->options['fmt']) && isset($query->settings['flags']['fmt'])){
456+
$query->options['fmt'] = $query->settings['flags']['fmt'];
436457
}
437458

438-
if(!isset($query->options['includeRids']) && isset($query->parent->settings['flags']['includeRids'])){
439-
$query->options['includeRids'] = $query->parent->settings['flags']['includeRids'];
459+
if(!isset($query->options['includeRids']) && isset($query->settings['flags']['includeRids'])){
460+
$query->options['includeRids'] = $query->settings['flags']['includeRids'];
440461
}
441462
}
442463

@@ -448,7 +469,11 @@ final public static function API_DoQuery(&$query){
448469
// final public static function API_GenAddRecordForm(&$query){ }
449470
// final public static function API_GenResultsTable(&$query){ }
450471
// final public static function API_GetAncestorInfo(&$query){ }
451-
// final public static function API_GetAppDTMInfo(&$query){ }
472+
473+
final public static function API_GetAppDTMInfo(&$query){
474+
$query->settings['flags']['dbidAsParam'] = true;
475+
}
476+
452477
// final public static function API_GetDBPage(&$query){ }
453478
// final public static function API_GetDBInfo(&$query){ }
454479
// final public static function API_GetDBVar(&$query){ }
@@ -500,8 +525,13 @@ class QuickBaseResponse {
500525

501526
final public static function API_Authenticate(&$query, &$results){
502527
$query->parent->settings['ticket'] = $results['ticket'];
528+
$query->settings['ticket'] = $results['ticket'];
529+
503530
$query->parent->settings['username'] = $query->options['username'];
531+
$query->settings['username'] = $query->options['username'];
532+
504533
$query->parent->settings['password'] = $query->options['password'];
534+
$query->settings['password'] = $query->options['password'];
505535
}
506536

507537
// final public static function API_ChangeGroupInfo(&$query, &$results){ }
@@ -654,11 +684,30 @@ final public static function API_GetSchema(&$query, &$results){
654684

655685
// final public static function API_GetRecordAsHTML(&$query, &$results){ }
656686
// final public static function API_GetRecordInfo(&$query, &$results){ }
657-
// final public static function API_GetRoleInfo(&$query, &$results){ }
687+
688+
final public static function API_GetRoleInfo(&$query, &$results){
689+
if(isset($results['roles']['id'])){
690+
$results['roles'] = array( $results['roles'] );
691+
}
692+
693+
for($i = 0, $l = count($results['roles']); $i < $l; ++$i){
694+
$results['roles'][$i]['access'] = array(
695+
'name' => $results['roles'][$i]['access']['_'],
696+
'id' => $results['roles'][$i]['access']['id']
697+
);
698+
}
699+
}
700+
658701
// final public static function API_GetUserInfo(&$query, &$results){ }
659702
// final public static function API_GetUserRole(&$query, &$results){ }
660703
// final public static function API_GetUsersInGroup(&$query, &$results){ }
661-
// final public static function API_GrantedDBs(&$query, &$results){ }
704+
705+
final public static function API_GrantedDBs(&$query, &$results){
706+
if(isset($results['databases']['dbinfo'])){
707+
$results['databases'] = $results['databases']['dbinfo'];
708+
}
709+
}
710+
662711
// final public static function API_GrantedDBsForGroup(&$query, &$results){ }
663712
// final public static function API_GrantedGroups(&$query, &$results){ }
664713
// final public static function API_ImportFromCSV(&$query, &$results){ }
@@ -676,7 +725,17 @@ final public static function API_GetSchema(&$query, &$results){
676725
// final public static function API_SetKeyField(&$query, &$results){ }
677726
// final public static function API_SignOut(&$query, &$results){ }
678727
// final public static function API_UploadFile(&$query, &$results){ }
679-
// final public static function API_UserRoles(&$query, &$results){ }
728+
729+
final public static function API_UserRoles(&$query, &$results){
730+
for($i = 0, $l = count($results['users']); $i < $l; ++$i){
731+
for($o = 0, $k = count($results['users'][$i]['roles']); $o < $k; ++$o){
732+
$results['users'][$i]['roles'][$o]['access'] = array(
733+
'name' => $results['users'][$i]['roles'][$o]['access']['_'],
734+
'id' => $results['users'][$i]['roles'][$o]['access']['id']
735+
);
736+
}
737+
}
738+
}
680739

681740
}
682741

tests/API_AddReplaceDBPage.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/* Copyright 2015 Tristian Flanagan
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
$expected = array(
19+
'action' => 'API_AddReplaceDBPage.js',
20+
'errcode' => 0,
21+
'errtext' => 'No error',
22+
'pageID' => 0
23+
);
24+
25+
$actual = $qb->api('API_AddReplaceDBPage', array(
26+
'dbid' => getenv('appid'),
27+
'pagename' => 'testpage.html',
28+
'pagetype' => 1,
29+
'pagebody' => '<html></html>'
30+
));
31+
32+
if(!objStrctMatch($actual, $expected)){
33+
throw new Exception('Mismatched API_AddReplaceDBPage Data Structure');
34+
}
35+
36+
?>

tests/API_CreateDatabase.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/* Copyright 2015 Tristian Flanagan
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
echo 'test skipped... ';
19+
20+
return;
21+
22+
$expected = array(
23+
'action' => 'API_CreateDatabase',
24+
'errcode' => 0,
25+
'errtext' => 'No error',
26+
'dbid' => '',
27+
'appdbid' => '',
28+
'apptoken' => ''
29+
);
30+
31+
$actual = $qb->api('API_CreateDatabase', array(
32+
'dbname' => 'Test DB',
33+
'dbdesc' => 'Testing DB from Node-QuickBase Tests',
34+
'createapptoken' => true
35+
));
36+
37+
if(!objStrctMatch($actual, $expected)){
38+
throw new Exception('Mismatched API_CreateDatabase Data Structure');
39+
}
40+
41+
$expected = array(
42+
'action' => 'API_DeleteDatabase',
43+
'errcode' => 0,
44+
'errtext' => 'No error'
45+
);
46+
47+
$actual = $qb->api('API_DeleteDatabase', array(
48+
'dbid' => $actual['appdbid']
49+
));
50+
51+
if(!objStrctMatch($actual, $expected)){
52+
throw new Exception('Mismatched API_DeleteDatabase Data Structure');
53+
}
54+
55+
?>

0 commit comments

Comments
 (0)