mirror of
https://github.com/nextcloud/server.git
synced 2025-01-30 22:37:01 +00:00
6ad6fa6425
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
168 lines
4.6 KiB
PHP
168 lines
4.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
namespace OC\Migration;
|
|
|
|
use OC\DB\Connection;
|
|
use OC\DB\MigrationService;
|
|
use OC\Migration\Exceptions\AttributeException;
|
|
use OCP\App\IAppManager;
|
|
use OCP\Migration\Attributes\GenericMigrationAttribute;
|
|
use OCP\Migration\Attributes\MigrationAttribute;
|
|
use Psr\Log\LoggerInterface;
|
|
use ReflectionClass;
|
|
|
|
/**
|
|
* Helps managing DB Migrations' Metadata
|
|
*
|
|
* @since 30.0.0
|
|
*/
|
|
class MetadataManager {
|
|
public function __construct(
|
|
private readonly IAppManager $appManager,
|
|
private readonly Connection $connection,
|
|
private readonly LoggerInterface $logger,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* We get all migrations from an app (or 'core'), and
|
|
* for each migration files we extract its attributes
|
|
*
|
|
* @param string $appId
|
|
*
|
|
* @return array<string, MigrationAttribute[]>
|
|
* @since 30.0.0
|
|
*/
|
|
public function extractMigrationAttributes(string $appId): array {
|
|
$ms = new MigrationService($appId, $this->connection);
|
|
|
|
$metadata = [];
|
|
foreach($ms->getAvailableVersions() as $version) {
|
|
$metadata[$version] = [];
|
|
$class = new ReflectionClass($ms->createInstance($version));
|
|
$attributes = $class->getAttributes();
|
|
foreach ($attributes as $attribute) {
|
|
$item = $attribute->newInstance();
|
|
if ($item instanceof MigrationAttribute) {
|
|
$metadata[$version][] = $item;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $metadata;
|
|
}
|
|
|
|
/**
|
|
* convert direct data from release metadata into a list of Migrations' Attribute
|
|
*
|
|
* @param array<array-key, array<array-key, array>> $metadata
|
|
* @param bool $filterKnownMigrations ignore metadata already done in local instance
|
|
*
|
|
* @return array{apps: array<array-key, array<string, MigrationAttribute[]>>, core: array<string, MigrationAttribute[]>}
|
|
* @since 30.0.0
|
|
*/
|
|
public function getMigrationsAttributesFromReleaseMetadata(
|
|
array $metadata,
|
|
bool $filterKnownMigrations = false
|
|
): array {
|
|
$appsAttributes = [];
|
|
foreach (array_keys($metadata['apps']) as $appId) {
|
|
if ($filterKnownMigrations && !$this->appManager->isInstalled($appId)) {
|
|
continue; // if not interested and app is not installed
|
|
}
|
|
|
|
$done = ($filterKnownMigrations) ? $this->getKnownMigrations($appId) : [];
|
|
$appsAttributes[$appId] = $this->parseMigrations($metadata['apps'][$appId] ?? [], $done);
|
|
}
|
|
|
|
$done = ($filterKnownMigrations) ? $this->getKnownMigrations('core') : [];
|
|
return [
|
|
'core' => $this->parseMigrations($metadata['core'] ?? [], $done),
|
|
'apps' => $appsAttributes
|
|
];
|
|
}
|
|
|
|
/**
|
|
* returns list of installed apps that does not support migrations metadata (yet)
|
|
*
|
|
* @param array<array-key, array<array-key, array>> $metadata
|
|
*
|
|
* @return string[]
|
|
* @since 30.0.0
|
|
*/
|
|
public function getUnsupportedApps(array $metadata): array {
|
|
return array_values(array_diff($this->appManager->getInstalledApps(), array_keys($metadata['apps'])));
|
|
}
|
|
|
|
/**
|
|
* convert raw data to a list of MigrationAttribute
|
|
*
|
|
* @param array $migrations
|
|
* @param array $ignoreMigrations
|
|
*
|
|
* @return array<string, MigrationAttribute[]>
|
|
*/
|
|
private function parseMigrations(array $migrations, array $ignoreMigrations = []): array {
|
|
$parsed = [];
|
|
foreach (array_keys($migrations) as $entry) {
|
|
if (in_array($entry, $ignoreMigrations)) {
|
|
continue;
|
|
}
|
|
|
|
$parsed[$entry] = [];
|
|
foreach ($migrations[$entry] as $item) {
|
|
try {
|
|
$parsed[$entry][] = $this->createAttribute($item);
|
|
} catch (AttributeException $e) {
|
|
$this->logger->warning('exception while trying to create attribute', ['exception' => $e, 'item' => json_encode($item)]);
|
|
$parsed[$entry][] = new GenericMigrationAttribute($item);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $parsed;
|
|
}
|
|
|
|
/**
|
|
* returns migrations already done
|
|
*
|
|
* @param string $appId
|
|
*
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private function getKnownMigrations(string $appId): array {
|
|
$ms = new MigrationService($appId, $this->connection);
|
|
return $ms->getMigratedVersions();
|
|
}
|
|
|
|
/**
|
|
* generate (deserialize) a MigrationAttribute from a serialized version
|
|
*
|
|
* @param array $item
|
|
*
|
|
* @return MigrationAttribute
|
|
* @throws AttributeException
|
|
*/
|
|
private function createAttribute(array $item): MigrationAttribute {
|
|
$class = $item['class'] ?? '';
|
|
$namespace = 'OCP\Migration\Attributes\\';
|
|
if (!str_starts_with($class, $namespace)
|
|
|| !ctype_alpha(substr($class, strlen($namespace)))) {
|
|
throw new AttributeException('class name does not looks valid');
|
|
}
|
|
|
|
try {
|
|
$attribute = new $class($item['table'] ?? '');
|
|
return $attribute->import($item);
|
|
} catch (\Error) {
|
|
throw new AttributeException('cannot import Attribute');
|
|
}
|
|
}
|
|
}
|