Skip to content

Commit

Permalink
Homepage|Build repository: Added JSON format reponse for "which packa…
Browse files Browse the repository at this point in the history
…ge" queries

The build repository is now able to answer the question "Which is the
latest version of Doomsday for my configuration?" when posed as an
http GET request. Rather than redirecting to the package download, a
JSON object graph containing all the metadata for the chosen package
is returned instead.
  • Loading branch information
danij-deng committed Feb 16, 2012
1 parent 2a7dfec commit 38f98a7
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 3 deletions.
70 changes: 70 additions & 0 deletions web/includes/utilities.inc.php
Expand Up @@ -139,3 +139,73 @@ function safe_url($raw_url)
// parse_url failed, or the scheme was not hypertext-based.
return false;
}

/**
* Given an associative array generate a textual representation
* using JSON markup.
*
* @note json_encode is practically useless for real world data
* in PHP <= 5.3 hence this hand-rolled imitation.
*
* @param array (Array) Array to be interpreted.
* @param flags (Integer) Reserved for future use.
* @param indent_level (Integer) Level of indent to add to the output.
* @return (Mixed) Textual representation in JSON format, else Boolean @c FALSE
*/
function json_encode_clean(&$array, $flags=0, $indent_level=0)
{
if(!is_array($array)) return FALSE;

$staged = NULL;
foreach($array as $key => $value)
{
$key = '"'. addslashes($key) .'"';

// Format the value:
if(is_array($value))
{
// Descend to the subobject.
$value = json_encode_clean($value, $flags, $indent_level+1);
}
else if(is_bool($value))
{
$value = ((boolean)$value)? 'true' : 'false';
}
else if(!is_numeric($value) || is_string($value))
{
$value = '"'. addslashes($value) .'"';
}

// Time to construct the staging array?
if(is_null($staged))
{
$staged = array();
}

$staged[] = "$key: $value";
}

if(is_null($staged)) return '';

// Collapse into JSON comma-delimited form.
$indent = ' ';
$glue = ", \n";
$tmp = '';
foreach($staged as $item)
{
$tmp .= $indent . $item . $glue;
}
$result = "{\n" . substr($tmp, 0, -strlen($glue)) . "\n}";

// Determine scope-level indent depth.
$indent_level = (integer)$indent_level;
if($indent_level < 0) $indent_level = 0;

// Apply a scope-level indent?
if($indent_level != 0)
{
$result = str_repeat($indent, $indent_level) . $result;
}

return $result;
}
1 change: 1 addition & 0 deletions web/plugins/buildrepository/builderproduct.interface.php
Expand Up @@ -28,6 +28,7 @@
interface iBuilderProduct
{
public function setBuildUniqueId($id);
public function buildUniqueId();
public function &compileLogUri();
public function compileWarnCount();
public function compileErrorCount();
Expand Down
42 changes: 39 additions & 3 deletions web/plugins/buildrepository/buildrepository.php
Expand Up @@ -730,15 +730,15 @@ public function InterpretRequest($request)
{
$this->initPackages();

// Are we redirecting to the download URI for a specific package?
// Are we redirecting to a specific package?
$getPackage = FALSE;
if(isset($uriArgs['platform']))
{
// Parse and validate arguments.
$platformId = $this->parsePlatformId($uriArgs['platform']);
$unstable = isset($uriArgs['unstable']);

// Default to downloading Doomsday if a pack is not specified.
// Default to Doomsday if a pack is not specified.
$packTitle = "Doomsday";
if(isset($uriArgs['pack']))
$packTitle = trim($uriArgs['pack']);
Expand All @@ -747,7 +747,21 @@ public function InterpretRequest($request)
$pack = &$this->choosePackage($platformId, $packTitle, $unstable);
if(!($pack instanceof NullPackage))
{
$FrontController->enqueueAction($this, array('getpackage' => $pack));
$args = array();

// Are we retrieving the object graph?
if(isset($uriArgs['graph']))
{
// Return the object graph.
$args['getgraph'] = $pack;
}
else
{
// Redirect to the package download.
$args['getpackage'] = $pack;
}

$FrontController->enqueueAction($this, $args);
return true; // Eat the request.
}
}
Expand Down Expand Up @@ -996,6 +1010,23 @@ private function outputPackageRedirect(&$pack)
$FrontController->endPage();
}

private function outputPackageGraph(&$pack)
{
global $FrontController;

if(!($pack instanceof AbstractPackage))
throw new Exception('Received invalid Package.');

// Generate a graph template for this package.
/// @todo cache the encoded graph!
$template = array();
$pack->populateGraphTemplate($template);
$json = json_encode_clean($template);

// Print to the output stream.
print($json);
}

private function countInstallablePackages(&$build)
{
$count = 0;
Expand Down Expand Up @@ -1042,6 +1073,11 @@ public function execute($args=NULL)
$this->outputPackageRedirect($args['getpackage']);
return;
}
else if(isset($args['getgraph']))
{
$this->outputPackageGraph($args['getgraph']);
return;
}

// Determine whether we are detailing a single build event or listing all events.
$uniqueId = $args['build'];
Expand Down
11 changes: 11 additions & 0 deletions web/plugins/buildrepository/packages/abstractpackage.class.php
Expand Up @@ -42,6 +42,16 @@ public function __construct($platformId=PID_ANY, $title=NULL, $version=NULL, $do
$this->downloadUri = "$downloadUri";
}

// Extends implementation in AbstractPackage.
public function populateGraphTemplate(&$tpl)
{
if(!is_array($tpl))
throw new Exception('Invalid template argument, array expected');

parent::populateGraphTemplate($tpl);
$tpl['download_uri'] = $this->downloadUri();
}

// Implements iDownloadable
public function &downloadUri()
{
Expand All @@ -52,6 +62,7 @@ public function &downloadUri()
return $this->downloadUri;
}

// Implements iDownloadable
public function hasDownloadUri()
{
return !is_null($this->downloadUri);
Expand Down
Expand Up @@ -67,12 +67,32 @@ public function composeFullTitle($includeVersion=true, $includeBuildId=true)
return $title;
}

// Extends implementation in AbstractPackage.
public function populateGraphTemplate(&$tpl)
{
if(!is_array($tpl))
throw new Exception('Invalid template argument, array expected');

parent::populateGraphTemplate($tpl);
$tpl['is_unstable'] = true;
$tpl['build_uniqueid'] = $this->buildUniqueId();
$tpl['compile_loguri'] = $this->compileLogUri();
$tpl['compile_errorcount'] = $this->compileErrorCount();
$tpl['compile_warncount'] = $this->compileWarnCount();
}

// Implements iBuilderProduct.
public function setBuildUniqueId($id)
{
$this->buildId = intval($id);
}

// Implements iBuilderProduct.
public function buildUniqueId()
{
return $this->buildId;
}

// Implements iBuilderProduct.
public function &compileLogUri()
{
Expand Down
19 changes: 19 additions & 0 deletions web/plugins/buildrepository/packages/basepackage.class.php
Expand Up @@ -75,4 +75,23 @@ public function __toString()
$fullTitle = $this->composeFullTitle();
return '('.get_class($this).":$title)";
}

/**
* Add the object graph properties for this to the specified template.
*
* @param tpl (Array) Array to be filled with graph properties.
*/
public function populateGraphTemplate(&$tpl)
{
if(!is_array($tpl))
throw new Exception('Invalid template argument, array expected');

$plat = &BuildRepositoryPlugin::platform($this->platformId());

$tpl['platform_id'] = $this->platformId();
$tpl['platform_name'] = $plat['nicename'];
$tpl['version'] = $this->version();
$tpl['title'] = $this->title();
$tpl['fulltitle'] = $this->composeFullTitle();
}
}

0 comments on commit 38f98a7

Please sign in to comment.