1
0
Fork 0
mirror of synced 2024-06-01 10:29:48 +12:00
appwrite/src/Appwrite/Migration/Migration.php

253 lines
7.5 KiB
PHP
Raw Normal View History

2021-01-14 05:51:02 +13:00
<?php
namespace Appwrite\Migration;
use Swoole\Runtime;
2022-01-19 00:05:04 +13:00
use Utopia\Database\Document;
use Utopia\Database\Database;
use Utopia\CLI\Console;
2022-01-19 00:05:04 +13:00
use Utopia\Config\Config;
2022-02-07 12:24:49 +13:00
use Exception;
2022-05-14 00:49:31 +12:00
use Utopia\App;
use Utopia\Database\Validator\Authorization;
2021-01-14 05:51:02 +13:00
abstract class Migration
{
2021-01-19 03:43:55 +13:00
/**
* @var int
*/
2022-01-19 00:05:04 +13:00
protected int $limit = 100;
2021-12-09 06:50:04 +13:00
/**
2022-01-19 00:05:04 +13:00
* @var Document
2021-12-09 06:50:04 +13:00
*/
2022-01-19 00:05:04 +13:00
protected Document $project;
2021-01-19 03:43:55 +13:00
/**
2022-01-19 00:05:04 +13:00
* @var Database
2021-01-19 03:43:55 +13:00
*/
2022-01-19 00:05:04 +13:00
protected Database $projectDB;
2021-01-19 03:43:55 +13:00
/**
2022-01-19 00:05:04 +13:00
* @var Database
2021-01-19 03:43:55 +13:00
*/
2022-01-19 00:05:04 +13:00
protected Database $consoleDB;
2021-07-02 21:09:02 +12:00
/**
* @var array
*/
public static array $versions = [
2022-01-19 00:05:04 +13:00
'0.13.0' => 'V12',
'0.13.1' => 'V12',
2022-03-08 00:33:09 +13:00
'0.13.2' => 'V12',
2022-03-16 01:19:04 +13:00
'0.13.3' => 'V12',
2022-03-24 01:05:57 +13:00
'0.13.4' => 'V12',
2022-05-09 06:53:20 +12:00
'0.14.0' => 'V13',
2022-05-19 03:09:06 +12:00
'0.14.1' => 'V13',
2021-07-02 21:09:02 +12:00
];
2022-02-18 07:24:50 +13:00
/**
* @var array
*/
protected array $collections;
public function __construct()
{
2022-05-14 00:49:31 +12:00
Authorization::disable();
Authorization::setDefaultStatus(false);
2022-02-18 07:24:50 +13:00
$this->collections = array_merge([
'_metadata' => [
2022-05-14 00:49:31 +12:00
'$id' => '_metadata',
'$collection' => Database::METADATA
2022-02-18 07:24:50 +13:00
],
'audit' => [
2022-05-14 00:49:31 +12:00
'$id' => 'audit',
'$collection' => Database::METADATA
2022-02-18 07:24:50 +13:00
],
'abuse' => [
2022-05-14 00:49:31 +12:00
'$id' => 'abuse',
'$collection' => Database::METADATA
2022-02-18 07:24:50 +13:00
]
], Config::getParam('collections', []));
}
/**
* Set project for migration.
*
2022-01-19 00:05:04 +13:00
* @param Document $project
* @param Database $projectDB
* @param Database $oldConsoleDB
*
2021-12-09 06:50:04 +13:00
* @return self
*/
2022-01-19 00:05:04 +13:00
public function setProject(Document $project, Database $projectDB, Database $consoleDB): self
{
$this->project = $project;
2022-01-19 00:05:04 +13:00
$this->projectDB = $projectDB;
2022-02-18 07:24:50 +13:00
$this->projectDB->setNamespace('_' . $this->project->getId());
2021-12-09 06:50:04 +13:00
2022-01-19 00:05:04 +13:00
$this->consoleDB = $consoleDB;
2021-12-09 06:50:04 +13:00
return $this;
}
/**
* Iterates through every document.
*
2021-01-19 03:43:55 +13:00
* @param callable $callback
*/
2021-01-21 22:57:15 +13:00
public function forEachDocument(callable $callback): void
{
2022-01-19 00:05:04 +13:00
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
2022-02-18 07:24:50 +13:00
foreach ($this->collections as $collection) {
2022-05-14 00:49:31 +12:00
if ($collection['$collection'] !== Database::METADATA) return;
2022-01-19 00:05:04 +13:00
$sum = 0;
$nextDocument = null;
$collectionCount = $this->projectDB->count($collection['$id']);
Console::log('Migrating Collection ' . $collection['$id'] . ':');
do {
$documents = $this->projectDB->find($collection['$id'], limit: $this->limit, cursor: $nextDocument);
$count = count($documents);
$sum += $count;
Console::log($sum . ' / ' . $collectionCount);
\Co\run(function (array $documents, callable $callback) {
foreach ($documents as $document) {
go(function (Document $document, callable $callback) {
if (empty($document->getId()) || empty($document->getCollection())) {
return;
2021-10-16 05:11:20 +13:00
}
2022-01-19 00:05:04 +13:00
$old = $document->getArrayCopy();
$new = call_user_func($callback, $document);
foreach ($document as &$attr) {
if ($attr instanceof Document) {
$attr = call_user_func($callback, $attr);
}
if (\is_array($attr)) {
foreach ($attr as &$child) {
if ($child instanceof Document) {
$child = call_user_func($callback, $child);
}
}
}
}
2022-01-19 00:05:04 +13:00
if (!$this->check_diff_multi($new->getArrayCopy(), $old)) {
return;
}
2022-01-19 00:05:04 +13:00
try {
$new = $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
} catch (\Throwable $th) {
Console::error('Failed to update document: ' . $th->getMessage());
return;
2022-01-19 00:05:04 +13:00
if ($document && $new->getId() !== $document->getId()) {
throw new Exception('Duplication Error');
}
}
2022-01-19 00:05:04 +13:00
}, $document, $callback);
}
}, $documents, $callback);
if ($count !== $this->limit) {
$nextDocument = null;
} else {
$nextDocument = end($documents);
}
2022-01-19 00:05:04 +13:00
} while (!is_null($nextDocument));
}
}
2021-12-09 06:50:04 +13:00
/**
* Checks 2 arrays for differences.
*
* @param array $array1
* @param array $array2
* @return array
*/
2021-12-13 23:59:13 +13:00
public function check_diff_multi(array $array1, array $array2): array
{
$result = array();
2021-10-16 05:11:20 +13:00
foreach ($array1 as $key => $val) {
if (is_array($val) && isset($array2[$key])) {
$tmp = $this->check_diff_multi($val, $array2[$key]);
if ($tmp) {
$result[$key] = $tmp;
}
} elseif (!isset($array2[$key])) {
$result[$key] = null;
} elseif ($val !== $array2[$key]) {
$result[$key] = $array2[$key];
}
2021-10-16 05:11:20 +13:00
if (isset($array2[$key])) {
unset($array2[$key]);
}
}
2021-10-16 05:11:20 +13:00
$result = array_merge($result, $array2);
2021-10-16 05:11:20 +13:00
return $result;
}
2022-05-14 00:49:31 +12:00
/**
* Creates colletion from the config collection.
*
* @param string $id
* @param string|null $name
* @return void
* @throws \Throwable
*/
protected function createCollection(string $id, string $name = null): void
{
$name ??= $id;
if (!$this->projectDB->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) {
$attributes = [];
$indexes = [];
$collection = $this->collections[$id];
foreach ($collection['attributes'] as $attribute) {
$attributes[] = new Document([
'$id' => $attribute['$id'],
'type' => $attribute['type'],
'size' => $attribute['size'],
'required' => $attribute['required'],
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
]);
}
foreach ($collection['indexes'] as $index) {
$indexes[] = new Document([
'$id' => $index['$id'],
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]);
}
try {
$this->projectDB->createCollection($name, $attributes, $indexes);
} catch (\Throwable $th) {
throw $th;
}
}
}
/**
* Executes migration for set project.
*/
abstract public function execute(): void;
2021-01-14 05:51:02 +13:00
}