Skip to content
This repository has been archived by the owner on Dec 16, 2020. It is now read-only.

Commit

Permalink
Merge pull request #245 from wmde/snapshot-all-data-migration
Browse files Browse the repository at this point in the history
Ultimate snapshot data migration command
  • Loading branch information
jakobw committed May 17, 2016
2 parents 73626a0 + 231a883 commit 58fc665
Show file tree
Hide file tree
Showing 10 changed files with 628 additions and 207 deletions.
10 changes: 10 additions & 0 deletions UPGRADE.md
Expand Up @@ -7,6 +7,16 @@ When upgrading Phragile to a newer version **always** run

after updating the files in the root directory of your application.

## From earlier releases to release 3.0.0

Snapshots created with Phragile versions earlier than 3.0.0 must be migrated in order to be used with Phragile 3.0.0.
In order to migrate snapshots

* make a backup of Phragile database
* run migration snapshot migration command:

php artisan snapshots:migrate

## From 1.1 release to 2.0.0

### Migrate your Snapshots
Expand Down
26 changes: 17 additions & 9 deletions app/Console/Commands/MigrateSnapshots.php
@@ -1,14 +1,15 @@
<?php namespace App\Console\Commands;

use App\Console\Commands\Lib\SnapshotDataConverter;
use App\Console\Commands\Lib\SnapshotTaskDataConverter;
use App\Console\Commands\Lib\SnapshotTransactionDataConverter;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use SprintSnapshot;

class MigrateSnapshots extends Command {

protected $name = 'snapshots:migrate';
protected $description = 'This command migrates all snapshots from the maniphest.query format to maniphest.search JSON.';
protected $description = 'Migrate all snapshots created with Phragile versions 1 and 2.';

public function fire()
{
Expand All @@ -27,16 +28,28 @@ public function fire()
}
}

$taskConverter = new SnapshotTaskDataConverter();
$transactionConverter = new SnapshotTransactionDataConverter();
$this->line('Migration in progress:');
$i = 0;
while (count($snapshots = $this->getSnapshotsPart($batchSize, $batchSize * $i)) !== 0)
{
foreach ($snapshots as $snapshot)
{
$snapshotData = json_decode($snapshot->data, true);
if ($snapshotData['tasks'] && $this->isManiphestQueryFormat($snapshotData['tasks']))
$converted = false;
if ($snapshotData['tasks'] && $taskConverter->needsConversion($snapshotData['tasks']))
{
$snapshotData['tasks'] = $taskConverter->convert($snapshotData['tasks']);
$converted = true;
}
if ($snapshotData['transactions'] && $transactionConverter->needsConversion($snapshotData['transactions']))
{
$snapshotData['transactions'] = $transactionConverter->convert($snapshotData['transactions']);
$converted = true;
}
if ($converted)
{
$snapshotData['tasks'] = (new SnapshotDataConverter($snapshotData['tasks']))->convert();
$snapshot->data = json_encode($snapshotData);
$snapshot->save();
}
Expand All @@ -61,11 +74,6 @@ private function getSnapshotsPart($limit, $offset)
return SprintSnapshot::take($limit)->skip($offset)->get();
}

private function isManiphestQueryFormat(array $taskData)
{
return array_keys($taskData) !== range(0, count($taskData) - 1);
}

/**
* Get the console command options.
*
Expand Down
72 changes: 0 additions & 72 deletions app/Console/Commands/lib/SnapshotDataConverter.php

This file was deleted.

93 changes: 93 additions & 0 deletions app/Console/Commands/lib/SnapshotTaskDataConverter.php
@@ -0,0 +1,93 @@
<?php

namespace App\Console\Commands\Lib;

use Phragile\Domain\Task;
use Phragile\TaskRawDataProcessor;

class SnapshotTaskDataConverter {

public function convert(array $taskData)
{
if ($this->taskDataIsInManiphestQueryFormat($taskData))
{
return array_values(array_map([$this, 'convertTaskInManiphestQueryFormat'], $taskData));
}
if (!$this->taskDataIsInManiphestSearchFormat($taskData))
{
return $taskData;
}
$processor = new TaskRawDataProcessor();
return array_map(
function(Task $task)
{
return $task->getData();
},
$processor->process($taskData)
);
}

public function needsConversion(array $taskData)
{
return $this->taskDataIsInManiphestSearchFormat($taskData) || $this->taskDataIsInManiphestQueryFormat($taskData);
}

private function taskDataIsInManiphestSearchFormat(array $taskData)
{
return array_keys($taskData) === range(0, count($taskData) - 1) && is_array($taskData[0]) &&
array_key_exists('fields', $taskData[0]) && array_key_exists('attachments', $taskData[0]);
}

private function taskDataIsInManiphestQueryFormat(array $taskData)
{
// task array in maniphest.query response is PHID-indexed
return array_keys($taskData) !== range(0, count($taskData) - 1);
}

private function convertTaskInManiphestQueryFormat(array $task)
{
$points = isset(
$task['auxiliary'][env('MANIPHEST_STORY_POINTS_FIELD')]
) ? $task['auxiliary'][env('MANIPHEST_STORY_POINTS_FIELD')] : 0;

return [
'id' => (int)$task['id'],
'title' => $task['title'],
'priority' => $task['priority'],
'status' => $task['status'],
'points' => $points,
'projectPHIDs' => $task['projectPHIDs'],
'assigneePHID' => $task['ownerPHID'],
'customFields' => $this->convertManiphestQueryCustomFields($task)
];
}

private function convertManiphestQueryCustomFields(array $task)
{
$fields = [];

foreach ($task['auxiliary'] as $name => $value)
{
$fields[$this->extractFieldName($name)] = $value;
}

return $fields;
}

/**
* Format of old custom field was e.g. std:maniphest:WMDE:story_points or isdc:sprint:storypoints.
* Equivalent new format would be custom.WMDE_story_points or custom.storypoints.
* @param string $s
* @return string
*/
private function extractFieldName($s)
{
if (count(explode(':', $s)) < 3)
{
return $s;
}
return implode(':',
array_slice(explode(':', $s), 2)
);
}
}
54 changes: 54 additions & 0 deletions app/Console/Commands/lib/SnapshotTransactionDataConverter.php
@@ -0,0 +1,54 @@
<?php

namespace App\Console\Commands\Lib;

use Phragile\Domain\Transaction;
use Phragile\TransactionRawDataProcessor;

class SnapshotTransactionDataConverter {

public function convert(array $transactionData)
{
if (!$this->transactionDataIsInPhabricatorFormat($transactionData))
{
return $transactionData;
}
return $this->convertPhabricatorTransactionData($transactionData);
}

private function convertPhabricatorTransactionData(array $transactionData)
{
$processor = new TransactionRawDataProcessor();
return array_map(
function(array $taskTransactions)
{
return array_map(
function(Transaction $transaction)
{
return $transaction->getData();
},
$taskTransactions
);
},
$processor->process($transactionData)
);
}
public function needsConversion(array $transactionData)
{
return $this->transactionDataIsInPhabricatorFormat($transactionData);
}

private function transactionDataIsInPhabricatorFormat(array $transactionData)
{
$taskId = array_keys($transactionData)[0];
if (!is_array($transactionData[$taskId]))
{
return false;
}
$transaction = array_values($transactionData[$taskId])[0];
return array_key_exists('taskID', $transaction) && array_key_exists('dateCreated', $transaction) &&
array_key_exists('transactionType', $transaction) && array_key_exists('oldValue', $transaction) &&
array_key_exists('newValue', $transaction);
}

}

0 comments on commit 58fc665

Please sign in to comment.