Skip to content

Commit

Permalink
Lib: Add dbidAsParam flag, isolate Query settings
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
tflanagan committed Oct 30, 2015
1 parent f1bdc79 commit ed5ce7c
Show file tree
Hide file tree
Showing 21 changed files with 835 additions and 48 deletions.
149 changes: 104 additions & 45 deletions quickbase.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class QuickBase {
'includeRids' => true,
'returnPercentage' => false,
'fmt' => 'structured',
'encoding' => 'UTF-8'
'encoding' => 'UTF-8',
'dbidAsParam' => false
),

'status' => array(
Expand All @@ -58,8 +59,6 @@ final public function api($action, $options = array()){
->actionRequest()
->constructPayload()
->transmit()
->processResponse()
->checkForAndHandleError()
->actionResponse();

return $query->response;
Expand Down Expand Up @@ -104,10 +103,10 @@ class QuickBaseQuery {
public $response = array();

protected $payload = '';
protected $xmlResponse = array();

public function __construct(&$parent, $action = '', $options = array()){
$this->parent = $parent;
$this->settings = array_replace_recursive(array(), $this->parent->settings);
$this->action = $action;
$this->options = $options;

Expand All @@ -131,20 +130,20 @@ final public function actionResponse(){
}

final public function addFlags(){
if(!isset($this->options['msInUTC']) && $this->parent->settings['flags']['msInUTC']){
if(!isset($this->options['msInUTC']) && $this->settings['flags']['msInUTC']){
$this->options['msInUTC'] = 1;
}

if(!isset($this->options['appToken']) && $this->parent->settings['appToken']){
$this->options['appToken'] = $this->parent->settings['appToken'];
if(!isset($this->options['appToken']) && $this->settings['appToken']){
$this->options['appToken'] = $this->settings['appToken'];
}

if(!isset($this->options['ticket']) && $this->parent->settings['ticket']){
$this->options['ticket'] = $this->parent->settings['ticket'];
if(!isset($this->options['ticket']) && $this->settings['ticket']){
$this->options['ticket'] = $this->settings['ticket'];
}

if(!isset($this->options['encoding']) && $this->parent->settings['flags']['encoding']){
$this->options['encoding'] = $this->parent->settings['flags']['encoding'];
if(!isset($this->options['encoding']) && $this->settings['flags']['encoding']){
$this->options['encoding'] = $this->settings['flags']['encoding'];
}

return $this;
Expand All @@ -153,7 +152,7 @@ final public function addFlags(){
final public function constructPayload(){
$this->payload = '';

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

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

$this->parent->settings['ticket'] = $newTicket['ticket'];
$this->settings['ticket'] = $newTicket['ticket'];
$this->options['ticket'] = $newTicket['ticket'];

return $this
->constructPayload()
->transmit()
->processResponse()
->checkForAndHandleError();
->transmit();
}catch(Exception $newTicketErr){
throw $newTicketErr;
}
Expand Down Expand Up @@ -217,35 +215,26 @@ final public function processOptions(){
return $this;
}

final public function processResponse(){
$this->response = array();

$this->xml2Arr($this->xmlResponse, $this->response);

$this->cleanXml2Arr($this->response);

return $this;
}

final public function transmit(){
$ch = curl_init(implode('', array(
$this->parent->settings['useSSL'] ? 'https://' : 'http://',
$this->parent->settings['realm'],
$this->settings['useSSL'] ? 'https://' : 'http://',
$this->settings['realm'],
'.',
$this->parent->settings['domain'],
$this->settings['domain'],
'/db/',
isset($this->options['dbid']) ? $this->options['dbid'] : 'main',
isset($this->options['dbid']) && !$this->settings['flags']['dbidAsParam'] ? $this->options['dbid'] : 'main',
'?act=',
$this->action,
$this->parent->settings['flags']['useXML'] ? '' : $this->payload
$this->settings['flags']['useXML'] ? '' : $this->payload
)));

curl_setopt($ch, CURLOPT_PORT, $this->parent->settings['useSSL'] ? 443 : 80);
curl_setopt($ch, CURLOPT_PORT, $this->settings['useSSL'] ? 443 : 80);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

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

$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);

curl_close($ch);

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

$this->xmlResponse = new SimpleXmlIterator($response);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);

self::parseCURLHeaders($headers);

if($headers['Content-Type'] === 'application/xml'){
$this->response = array();

$xml = new SimpleXmlIterator($body);

$this->xml2Arr($xml, $this->response);

$this->cleanXml2Arr($this->response);

$this->checkForAndHandleError();
}else{
$this->response = $body;
}

return $this;
}
Expand Down Expand Up @@ -355,6 +363,19 @@ final protected static function cleanXml2Arr(&$arr){
}
}

final protected static function parseCURLHeaders(&$headers){
$newHeaders = array();
$headers = explode("\r\n", $headers);

foreach($headers as $header){
$i = strpos($header, ':');

$newHeaders[substr($header, 0, $i)] = substr($header, $i + 2);
}

$headers = $newHeaders;
}

final protected static function xml2Arr($xml, &$arr){
for($xml->rewind(); $xml->valid(); $xml->next()){
$key = $xml->key();
Expand Down Expand Up @@ -408,7 +429,7 @@ class QuickBaseRequest {

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

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

final public static function API_DoQuery(&$query){
if(!isset($query->options['returnPercentage']) && isset($query->parent->settings['flags']['returnPercentage'])){
$query->options['returnPercentage'] = $query->parent->settings['flags']['returnPercentage'];
if(!isset($query->options['returnPercentage']) && isset($query->settings['flags']['returnPercentage'])){
$query->options['returnPercentage'] = $query->settings['flags']['returnPercentage'];
}

if(!isset($query->options['fmt']) && isset($query->parent->settings['flags']['fmt'])){
$query->options['fmt'] = $query->parent->settings['flags']['fmt'];
if(!isset($query->options['fmt']) && isset($query->settings['flags']['fmt'])){
$query->options['fmt'] = $query->settings['flags']['fmt'];
}

if(!isset($query->options['includeRids']) && isset($query->parent->settings['flags']['includeRids'])){
$query->options['includeRids'] = $query->parent->settings['flags']['includeRids'];
if(!isset($query->options['includeRids']) && isset($query->settings['flags']['includeRids'])){
$query->options['includeRids'] = $query->settings['flags']['includeRids'];
}
}

Expand All @@ -448,7 +469,11 @@ final public static function API_DoQuery(&$query){
// final public static function API_GenAddRecordForm(&$query){ }
// final public static function API_GenResultsTable(&$query){ }
// final public static function API_GetAncestorInfo(&$query){ }
// final public static function API_GetAppDTMInfo(&$query){ }

final public static function API_GetAppDTMInfo(&$query){
$query->settings['flags']['dbidAsParam'] = true;
}

// final public static function API_GetDBPage(&$query){ }
// final public static function API_GetDBInfo(&$query){ }
// final public static function API_GetDBVar(&$query){ }
Expand Down Expand Up @@ -500,8 +525,13 @@ class QuickBaseResponse {

final public static function API_Authenticate(&$query, &$results){
$query->parent->settings['ticket'] = $results['ticket'];
$query->settings['ticket'] = $results['ticket'];

$query->parent->settings['username'] = $query->options['username'];
$query->settings['username'] = $query->options['username'];

$query->parent->settings['password'] = $query->options['password'];
$query->settings['password'] = $query->options['password'];
}

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

// final public static function API_GetRecordAsHTML(&$query, &$results){ }
// final public static function API_GetRecordInfo(&$query, &$results){ }
// final public static function API_GetRoleInfo(&$query, &$results){ }

final public static function API_GetRoleInfo(&$query, &$results){
if(isset($results['roles']['id'])){
$results['roles'] = array( $results['roles'] );
}

for($i = 0, $l = count($results['roles']); $i < $l; ++$i){
$results['roles'][$i]['access'] = array(
'name' => $results['roles'][$i]['access']['_'],
'id' => $results['roles'][$i]['access']['id']
);
}
}

// final public static function API_GetUserInfo(&$query, &$results){ }
// final public static function API_GetUserRole(&$query, &$results){ }
// final public static function API_GetUsersInGroup(&$query, &$results){ }
// final public static function API_GrantedDBs(&$query, &$results){ }

final public static function API_GrantedDBs(&$query, &$results){
if(isset($results['databases']['dbinfo'])){
$results['databases'] = $results['databases']['dbinfo'];
}
}

// final public static function API_GrantedDBsForGroup(&$query, &$results){ }
// final public static function API_GrantedGroups(&$query, &$results){ }
// final public static function API_ImportFromCSV(&$query, &$results){ }
Expand All @@ -676,7 +725,17 @@ final public static function API_GetSchema(&$query, &$results){
// final public static function API_SetKeyField(&$query, &$results){ }
// final public static function API_SignOut(&$query, &$results){ }
// final public static function API_UploadFile(&$query, &$results){ }
// final public static function API_UserRoles(&$query, &$results){ }

final public static function API_UserRoles(&$query, &$results){
for($i = 0, $l = count($results['users']); $i < $l; ++$i){
for($o = 0, $k = count($results['users'][$i]['roles']); $o < $k; ++$o){
$results['users'][$i]['roles'][$o]['access'] = array(
'name' => $results['users'][$i]['roles'][$o]['access']['_'],
'id' => $results['users'][$i]['roles'][$o]['access']['id']
);
}
}
}

}

Expand Down
36 changes: 36 additions & 0 deletions tests/API_AddReplaceDBPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/* Copyright 2015 Tristian Flanagan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

$expected = array(
'action' => 'API_AddReplaceDBPage.js',
'errcode' => 0,
'errtext' => 'No error',
'pageID' => 0
);

$actual = $qb->api('API_AddReplaceDBPage', array(
'dbid' => getenv('appid'),
'pagename' => 'testpage.html',
'pagetype' => 1,
'pagebody' => '<html></html>'
));

if(!objStrctMatch($actual, $expected)){
throw new Exception('Mismatched API_AddReplaceDBPage Data Structure');
}

?>
55 changes: 55 additions & 0 deletions tests/API_CreateDatabase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/* Copyright 2015 Tristian Flanagan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

echo 'test skipped... ';

return;

$expected = array(
'action' => 'API_CreateDatabase',
'errcode' => 0,
'errtext' => 'No error',
'dbid' => '',
'appdbid' => '',
'apptoken' => ''
);

$actual = $qb->api('API_CreateDatabase', array(
'dbname' => 'Test DB',
'dbdesc' => 'Testing DB from Node-QuickBase Tests',
'createapptoken' => true
));

if(!objStrctMatch($actual, $expected)){
throw new Exception('Mismatched API_CreateDatabase Data Structure');
}

$expected = array(
'action' => 'API_DeleteDatabase',
'errcode' => 0,
'errtext' => 'No error'
);

$actual = $qb->api('API_DeleteDatabase', array(
'dbid' => $actual['appdbid']
));

if(!objStrctMatch($actual, $expected)){
throw new Exception('Mismatched API_DeleteDatabase Data Structure');
}

?>
Loading

0 comments on commit ed5ce7c

Please sign in to comment.