Skip to content
This repository has been archived by the owner on Jun 18, 2024. It is now read-only.

Various improvements and JSON-friendly additions #24

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
'server' => 'localhost',
'port' => 3306,
'type' => 'mysql',
'table_blacklist' => array(),
'column_blacklist' => array(),
'table_blocklist' => array(),
'column_blocklist' => array(),
'table_allowlist' => array(),
);

register_db_api( 'dataset-name', $args );

*/
*/
Binary file removed includes/.DS_Store
Binary file not shown.
76 changes: 46 additions & 30 deletions includes/class.db-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class DB_API {
public $ttl = 3600;
public $cache = array();
public $connections = array();
public $format = 'json';

function __construct() {

Expand Down Expand Up @@ -40,8 +41,9 @@ function register_db( $name = null, $args = array() ) {
'server' => 'localhost',
'port' => 3306,
'type' => 'mysql',
'table_blacklist' => array(),
'column_blacklist' => array(),
'table_blocklist' => array(),
'table_allowlist' => array(),
'column_blocklist' => array(),
'ttl' => $this->ttl,
);

Expand Down Expand Up @@ -166,9 +168,13 @@ function parse_query( $query = null ) {

$db = $this->get_db( $parts['db'] );

if ( in_array( $parts['table'], $db->table_blacklist ) ) {
if ( in_array( $parts['table'], $db->table_blocklist ) ) {
$this->error( 'Invalid table', 404 );
}

if ( sizeof($db->table_allowlist)>0 && !in_array( $parts['table'], $db->table_allowlist ) ) {
$this->error( 'Invalid method: '.$parts['table'], 404 );
}

if ( !in_array( $parts['direction'], array( 'ASC', 'DESC' ) ) ) {
$parts['direction'] = null;
Expand All @@ -177,7 +183,7 @@ function parse_query( $query = null ) {
if ( !in_array( $parts['format'], array( 'html', 'xml', 'json' ) ) ) {
$parts['format'] = null;
}

return $parts;

}
Expand Down Expand Up @@ -335,6 +341,8 @@ function get_first_column( $table, $db = null ) {
* @return array an array of results
*/
function query( $query, $db = null ) {

$this->format = $query['format'];

$key = md5( serialize( $query ) . $this->get_db( $db )->name );

Expand Down Expand Up @@ -400,18 +408,18 @@ function query( $query, $db = null ) {
}

/**
* Remove any blacklisted columns from the data set.
* Remove any blocklisted columns from the data set.
*/
function sanitize_results( $results, $db = null ) {

$db = $this->get_db( $db );

if ( empty( $db->column_blacklist ) ) {
if ( empty( $db->column_blocklist ) ) {
return $results;
}

foreach ( $results as $ID => $result ) {
foreach ( $db->column_blacklist as $column ) {
foreach ( $db->column_blocklist as $column ) {
unset( $results[ $ID ] -> $column );
}

Expand All @@ -427,11 +435,16 @@ function sanitize_results( $results, $db = null ) {
* @param int $code (optional) the error code with which to respond
*/
function error( $error, $code = '500' ) {

if ( is_object( $error ) && method_exists( $error, 'get_message' ) ) {
$error = $error->get_message();
}


if ( is_object( $error ) && method_exists( $error, 'get_message' ) ) {
$error = $error->get_message();
}

if('json'==$this->format) {
$this->render_json(['error'=>$error], []);
exit;
}

http_response_code( $code );
die( $error );
return false;
Expand All @@ -443,6 +456,9 @@ function error( $error, $code = '500' ) {
* @todo Support JSONP, with callback filtering.
*/
function render_json( $data, $query ) {
if(false === $data) {
$data = ['error'=>"No data found"];
}

header('Content-type: application/json');
$output = json_encode( $data );
Expand Down Expand Up @@ -480,9 +496,9 @@ function jsonp_callback_filter( $callback ) {
*/
function render_html( $data ) {

require_once( dirname( __FILE__ ) . '/bootstrap/header.html' );
require_once( dirname( __FILE__ ) . '/bootstrap/header.html' );

//err out if no results
//err out if no results
if ( empty( $data ) ) {
$this->error( 'No results found', 404 );
return;
Expand All @@ -495,29 +511,29 @@ function render_html( $data ) {
echo "<table class='table table-striped'>\n<thead>\n<tr>\n";

foreach ( array_keys( get_object_vars( reset( $data ) ) ) as $heading ) {
echo "\t<th>$heading</th>\n";
echo "\t<th>$heading</th>\n";
}

echo "</tr>\n</thead>\n";

//loop data and render
foreach ( $data as $row ) {
echo "<tr>\n";
foreach ( $row as $cell ) {
echo "\t<td>$cell</td>\n";
}
echo "</tr>";
echo "<tr>\n";
foreach ( $row as $cell ) {
echo "\t<td>$cell</td>\n";
}
echo "</tr>";
}

echo "</table>";

require_once( dirname( __FILE__ ) . '/bootstrap/footer.html' );
require_once( dirname( __FILE__ ) . '/bootstrap/footer.html' );

}

Expand Down Expand Up @@ -576,13 +592,13 @@ function object_to_xml( $array, $xml ) {
* Clean up XML domdocument formatting and return as string
*/
function tidy_xml( $xml ) {
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML( $xml->asXML() );
return $dom->saveXML();
}

/**
Expand Down Expand Up @@ -627,4 +643,4 @@ function cache_set( $key, $value, $ttl = null ) {
}


$db_api = new DB_API();
$db_api = new DB_API();
37 changes: 33 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ $args = array(
'server' => 'localhost',
'port' => 3306,
'type' => 'mysql',
'table_blacklist' => array(),
'column_blacklist' => array(),
'table_blocklist' => array(),
'column_blocklist' => array(),
'table_allowlist' => array(),
);

register_db_api( 'dataset_name', $args );
Expand All @@ -97,8 +98,9 @@ $args = array(
'server' => 'localhost',
'port' => 3306,
'type' => 'mysql',
'table_blacklist' => array('cache', 'passwords'),
'column_blacklist' => array('password_hint'),
'table_blocklist' => array('cache', 'passwords'),
'column_blocklist' => array('password_hint'),
'table_allowlist' => array(),
);

register_db_api( 'facility-inspections', $args );
Expand All @@ -107,6 +109,33 @@ register_db_api( 'facility-inspections', $args );

Retrieving the contents of the table `history` within this dataset as JSON would be accomplished with a request for `/facility-inspections/history.json`. Note that it is the name of the dataset (`facility-inspections`) and not the name of the database (`inspections`) that is specified in the URL.

Using the Allowlist
--------------------
If you specify tables via table_allowlist, it will prevent exposing *all* other tables. If you do not want to use the allowlist, you can leave it blank, i.e. ``'table_allowlist' => array()`` and it will be ignored.

```php

$args = array(
'name' => 'inspections',
'username' => 'website',
'password' => 's3cr3tpa55w0rd',
'server' => 'localhost',
'port' => 3306,
'type' => 'mysql',
'table_blocklist' => array(),
'column_blocklist' => array('password_hint'),
'table_allowlist' => array('data_public', 'moredata_public'),
);

register_db_api( 'api_v1', $args );
```

Using the allowlist, retreiving the contents of the table `data_public` within this dataset as JSON would be accomplished with a request for `/api_v1/data_public.json`. Note that it is the name of the dataset (`api_v1`) and not the name of the database (`inspections`) that is specified in the URL.

In the above example, using `/api_v1/users.json` would fail, even if there were a users table, because it is not specified in the allowlist.

Other Datasets
--------------
For a SQLite database, simply provide the path to the database in `name`.

For an Oracle database, you can either specify a service defined in tsnames.ora (e.g. `dept_spending`) or you can define an Oracle Instant Client connection string (e.g., `//localhost:1521/dept_spending`).
Expand Down