123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- <?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');
- }
- }
- }
|