Skip to content
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
156 changes: 104 additions & 52 deletions src/DrupalReleaseDate/Controllers/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use DateTime;
use DateInterval;

use Doctrine\DBAL\Connection;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -14,27 +15,48 @@ class Data
/**
* Parse a version string from the request's GET parameters.
*
* Accepts either a string containing only a major version number, or major
* and minor version numbers separated by a period. If only a major version
* is provided, the return value is normalized to include '0' as the minor
* version.
* 'version' accepts either a string containing only a major version number,
* or major and minor version numbers separated by a period. If only a
* major version is provided, the return value is normalized to include '0'
* as the minor version.
*
* @param Request $request
* @return string
* @return array
*
* @throws \Exception
*/
public static function parseVersionFromRequest(Request $request)
{
$versionString = $request->query->get('version', '8.0');
if (!preg_match('/^([0-9]+)(\\.[0-9]+)?$/', $versionString)) {
throw new \Exception("Invalid version parameter");
if (($versionString = $request->query->get('version', false)) !== false) {
if (!preg_match('/^([0-9]+)(\\.[0-9]+)?$/', $versionString)) {
throw new \Exception("Invalid version parameter");
}
$segments = explode('.', $versionString);
$major = $segments[0];
$minor = empty($segments[1]) ? '0' : $segments[1];

return array(
'major' => $major,
'minor' => $minor,
);
} elseif (($majorVersion = $request->query->get('version_major', false)) !== false) {
$version = array(
'major' => (int) $majorVersion,
);

if (($minorVersion = $request->query->get('version_minor', false)) !== false) {
$version['minor'] = $minorVersion;
}

return $version;
} elseif (($minorVersion = $request->query->get('version_minor', false)) !== false) {
throw new \Exception("Major version must be specified when minor version is provided.");
}
$segments = explode('.', $versionString);
$major = $segments[0];
$minor = empty($segments[1])? '0' : $segments[1];

return $major . '.' . $minor;
return array(
'major' => 8,
'minor' => 0,
);
}

/**
Expand All @@ -44,7 +66,8 @@ public static function parseVersionFromRequest(Request $request)
* @param string $key
* @return \DateTime
*/
public static function parseDateFromRequest(Request $request, $key) {
public static function parseDateFromRequest(Request $request, $key)
{
$value = null;
if ($request->query->has($key)) {
$value = new DateTime($request->query->get($key));
Expand All @@ -55,30 +78,33 @@ public static function parseDateFromRequest(Request $request, $key) {

public function samples(Application $app, Request $request)
{
/** @var Connection $db */
$db = $app['db'];

$responseData = array();
try {
$versionString = self::parseVersionFromRequest($request);
$version = self::parseVersionFromRequest($request);
} catch (\Exception $e) {
$app->abort(400, 'Invalid version parameters');
}
catch (\Exception $e) {
$app->abort(400, 'Invalid "version" parameter');
$responseData['version_major'] = $version['major'];
if (isset($version['minor'])) {
$responseData['version_minor'] = $version['minor'];
}
$responseData['version'] = $versionString;


try {
if ($from = self::parseDateFromRequest($request, 'from')) {
if (($from = self::parseDateFromRequest($request, 'from'))) {
$responseData['from'] = $from->format(DateTime::ATOM);
}
}
catch (\Exception $e) {
} catch (\Exception $e) {
$app->abort(400, 'Invalid "from" parameter');
}
try {
if($to = self::parseDateFromRequest($request, 'to')) {
if (($to = self::parseDateFromRequest($request, 'to'))) {
$responseData['to'] = $to->format(DateTime::ATOM);
}
}
catch (\Exception $e) {
} catch (\Exception $e) {
$app->abort(400, 'Invalid "to" parameter');
}

Expand All @@ -87,37 +113,43 @@ public function samples(Application $app, Request $request)
}

// Check against Last-Modified header.
$lastQuery = $app['db']->createQueryBuilder()
$lastQuery = $db->createQueryBuilder()
->select('s.when')
->from('samples', 's')
->where('s.version = :version')
->setParameter('version', $app['db']->convertToDatabaseValue($versionString, 'string'), \PDO::PARAM_STR)
->orderBy($app['db']->quoteIdentifier('when'), 'DESC')
->where('s.version_major = :version_major')
->setParameter('version_major', $version['major'], \PDO::PARAM_INT)
->orderBy($db->quoteIdentifier('when'), 'DESC')
->setMaxResults(1);

if (isset($version['minor'])) {
$lastQuery
->andWhere('s.version_minor = :version_minor')
->setParameter('version_minor', $version['minor'], \PDO::PARAM_INT);
}
if ($from) {
$lastQuery
->andWhere('s.when >= :from')
->setParameter('from', $app['db']->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR);
->setParameter('from', $db->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR);
}
if ($to) {
$lastQuery
->andWhere('s.when <= :to')
->setParameter('to', $app['db']->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR);
->setParameter('to', $db->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR);
}

$lastResults = $lastQuery->execute();
$lastDate = null;
$cacheMaxAge = 3600;
if ($lastResultRow = $lastResults->fetch(\PDO::FETCH_ASSOC)) {
if (($lastResultRow = $lastResults->fetch(\PDO::FETCH_ASSOC))) {
$lastDate = new DateTime($lastResultRow['when']);
$responseData['modified'] = $lastDate->format(DateTime::ATOM);

$nowDate = new DateTime();
if ($to && $to < $nowDate){
// If the request limits to data in the past, we can set a very long expiry since the results will never change.
if ($to && $to < $nowDate) {
// If the request limits to data in the past, we can set a very
// long expiry since the results will never change.
$cacheMaxAge = 31536000;
}
else {
} else {
// Calculate cache max age based on the time to the next sample.
$nextSampleDate = clone $lastDate;
$nextSampleDate->add(new DateInterval('PT' . $app['config']['sample.interval'] . 'S'));
Expand All @@ -136,25 +168,33 @@ public function samples(Application $app, Request $request)
}
}

$sampleValuesQuery = $app['db']->createQueryBuilder()
$sampleValuesQuery = $db->createQueryBuilder()
->select(
'sv.version_major',
'sv.version_minor',
'sv.when',
'sv.key',
'sv.value'
)
->from('sample_values', 'sv')
->where('sv.version = :version')
->setParameter('version', $app['db']->convertToDatabaseValue($versionString, 'string'), \PDO::PARAM_STR)
->orderBy($app['db']->quoteIdentifier('when'), 'ASC');
->where('sv.version_major = :version_major')
->setParameter('version_major', $version['major'], \PDO::PARAM_INT)
->orderBy($db->quoteIdentifier('when'), 'ASC');

if (isset($version['minor'])) {
$sampleValuesQuery
->andWhere('sv.version_minor = :version_minor')
->setParameter('version_minor', $version['minor'], \PDO::PARAM_INT);
}
if ($from) {
$sampleValuesQuery
->andWhere('sv.when >= :from')
->setParameter('from', $app['db']->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR);
->setParameter('from', $db->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR);
}
if ($to) {
$sampleValuesQuery
->andWhere('sv.when <= :to')
->setParameter('to', $app['db']->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR);
->setParameter('to', $db->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR);
}

if ($request->query->has('limit')) {
Expand All @@ -164,22 +204,28 @@ public function samples(Application $app, Request $request)
}
$responseData['limit'] = $limit;

$dateLimitQuery = $app['db']->createQueryBuilder()
$dateLimitQuery = $db->createQueryBuilder()
->select('s.when')
->from('samples', 's')
->where('s.version = :version')
->setParameter('version', $app['db']->convertToDatabaseValue($versionString, 'string'), \PDO::PARAM_STR)
->orderBy($app['db']->quoteIdentifier('when'), 'DESC')
->where('s.version_major = :version_major')
->setParameter('version_major', $version['major'], \PDO::PARAM_INT)
->orderBy($db->quoteIdentifier('when'), 'DESC')
->setMaxResults($limit);

if (isset($version['minor'])) {
$dateLimitQuery
->andWhere('s.version_minor = :version_minor')
->setParameter('version_minor', $version['minor'], \PDO::PARAM_INT);
}
if ($from) {
$dateLimitQuery
->andWhere('s.when >= :from')
->setParameter('from', $app['db']->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR);
->setParameter('from', $db->convertToDatabaseValue($from, 'datetime'), \PDO::PARAM_STR);
}
if ($to) {
$dateLimitQuery
->andWhere('s.when <= :to')
->setParameter('to', $app['db']->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR);
->setParameter('to', $db->convertToDatabaseValue($to, 'datetime'), \PDO::PARAM_STR);
}

$dateLimitResults = $dateLimitQuery
Expand Down Expand Up @@ -207,15 +253,21 @@ public function samples(Application $app, Request $request)
$sampleValuesResult = $sampleValuesQuery->execute();

$data = array();
while ($sampleValueRow = $sampleValuesResult->fetch(\PDO::FETCH_ASSOC)) {
$valueWhen = $app['db']->convertToPhpValue($sampleValueRow['when'], 'datetime');
$valueWhenTimestamp = $valueWhen->getTimestamp();
if (!isset($data[$valueWhenTimestamp])) {
$data[$valueWhenTimestamp] = array(
while (($sampleValueRow = $sampleValuesResult->fetch(\PDO::FETCH_ASSOC))) {
$valueWhen = $db->convertToPhpValue($sampleValueRow['when'], 'datetime');
$sampleKey = implode(array(
$sampleValueRow['version_major'],
$sampleValueRow['version_minor'],
$valueWhen->getTimestamp(),
));
if (!isset($data[$sampleKey])) {
$data[$sampleKey] = array(
'version_major' => $sampleValueRow['version_major'],
'version_minor' => $sampleValueRow['version_minor'],
'when' => $valueWhen->format(DateTime::ATOM),
);
}
$data[$valueWhenTimestamp][$sampleValueRow['key']] = $app['db']->convertToPhpValue($sampleValueRow['value'], 'smallint');
$data[$sampleKey][$sampleValueRow['key']] = $db->convertToPhpValue($sampleValueRow['value'], 'smallint');
}
$responseData['data'] = array_values($data);

Expand Down
73 changes: 67 additions & 6 deletions src/DrupalReleaseDate/Installation.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,27 +154,30 @@ protected function installSchema()
$schema = new Schema();

$samples = $schema->createTable('samples');
$samples->addColumn('version', 'string', array('length' => 32));
$samples->addColumn('version_major', 'smallint');
$samples->addColumn('version_minor', 'smallint');
$samples->addColumn('when', 'datetime');
$samples->addColumn('notes', 'string', array('default' => ''));
$samples->setPrimaryKey(array('version', 'when'));
$samples->setPrimaryKey(array('version_major', 'version_minor', 'when'));

$sample_values = $schema->createTable('sample_values');
$sample_values->addColumn('version', 'string', array('length' => 32));
$sample_values->addColumn('version_major', 'smallint');
$sample_values->addColumn('version_minor', 'smallint');
$sample_values->addColumn('when', 'datetime');
$sample_values->addColumn('key', 'string', array('length' => 64));
$sample_values->addColumn('value', 'smallint', array('notnull' => false));
$sample_values->setPrimaryKey(array('version', 'when', 'key'));
$sample_values->setPrimaryKey(array('version_major', 'version_minor', 'when', 'key'));

$estimates = $schema->createTable('estimates');
$estimates->addColumn('version', 'string', array('length' => 32));
$estimates->addColumn('version_major', 'smallint');
$estimates->addColumn('version_minor', 'smallint');
$estimates->addColumn('when', 'datetime');
$estimates->addColumn('started', 'datetime');
$estimates->addColumn('completed', 'datetime', array('notnull' => false));
$estimates->addColumn('estimate', 'datetime', array('notnull' => false));
$estimates->addColumn('note', 'string', array('default' => ''));
$estimates->addColumn('data', 'blob');
$estimates->setPrimaryKey(array('version', 'when'));
$estimates->setPrimaryKey(array('version_major', 'version_minor', 'when'));

$state = $schema->createTable('state');
$state->addColumn('key', 'string', array('length' => 255));
Expand Down Expand Up @@ -437,4 +440,62 @@ protected function update_7()

unlink($this->configDir . '/InstallationVersion');
}

/**
* Split string version fields to major + minor integers.
*/
protected function update_8()
{
$synchronizer = new SingleDatabaseSynchronizer($this->db);

$schema = $this->db->getSchemaManager()->createSchema();
$schema = clone $schema;

// Add fields.
$samplesTable = $schema->getTable('samples');
$samplesTable->addColumn('version_major', 'smallint');
$samplesTable->addColumn('version_minor', 'smallint');

$sampleValuesTable = $schema->getTable('sample_values');
$sampleValuesTable->addColumn('version_major', 'smallint');
$sampleValuesTable->addColumn('version_minor', 'smallint');

$estimatesTable = $schema->getTable('estimates');
$estimatesTable->addColumn('version_major', 'smallint');
$estimatesTable->addColumn('version_minor', 'smallint');

$synchronizer->updateSchema($schema);

// Copy values to new fields.
$this->db->createQueryBuilder()
->update('samples')
->set('version_major', 'SUBSTRING(version FROM 1 FOR 1)')
->set('version_minor', 'SUBSTRING(version FROM 3 FOR 1)')
->execute();
$this->db->createQueryBuilder()
->update('sample_values')
->set('version_major', 'SUBSTRING(version FROM 1 FOR 1)')
->set('version_minor', 'SUBSTRING(version FROM 3 FOR 1)')
->execute();
$this->db->createQueryBuilder()
->update('estimates')
->set('version_major', 'SUBSTRING(version FROM 1 FOR 1)')
->set('version_minor', 'SUBSTRING(version FROM 3 FOR 1)')
->execute();

// Update primary keys.
$samplesTable->dropPrimaryKey();
$samplesTable->setPrimaryKey(array('version_major', 'when', 'version_minor'));
$sampleValuesTable->dropPrimaryKey();
$sampleValuesTable->setPrimaryKey(array('version_major', 'when', 'key', 'version_minor'));
$estimatesTable->dropPrimaryKey();
$estimatesTable->setPrimaryKey(array('version_major', 'when', 'version_minor'));
$synchronizer->updateSchema($schema);

// Drop old version column.
$samplesTable->dropColumn('version');
$sampleValuesTable->dropColumn('version');
$estimatesTable->dropColumn('version');
$synchronizer->updateSchema($schema);
}
}