From 88cfab4f329c7ee1f360be1ad7d90cf767da3720 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Wed, 17 Jul 2024 10:49:11 -0100 Subject: [PATCH 1/5] feat(upgrade): migration attributes Signed-off-by: Maxence Lange d Signed-off-by: Maxence Lange f Signed-off-by: Maxence Lange d Signed-off-by: Maxence Lange --- .../Db/Migrations/GenerateMetadataCommand.php | 106 ++++++++++++ core/Command/Db/Migrations/PreviewCommand.php | 162 ++++++++++++++++++ core/register_command.php | 2 + lib/composer/composer/autoload_classmap.php | 20 +++ lib/composer/composer/autoload_static.php | 20 +++ lib/public/Migration/Attributes/AddColumn.php | 22 +++ lib/public/Migration/Attributes/AddIndex.php | 20 +++ .../Attributes/ColumnMigrationAttribute.php | 58 +++++++ .../Migration/Attributes/ColumnType.php | 46 +++++ .../Migration/Attributes/CreateTable.php | 20 +++ .../Migration/Attributes/DeleteTable.php | 18 ++ .../Migration/Attributes/DropColumn.php | 21 +++ lib/public/Migration/Attributes/DropIndex.php | 20 +++ .../Attributes/GenericMigrationAttribute.php | 31 ++++ .../Attributes/IndexMigrationAttribute.php | 46 +++++ lib/public/Migration/Attributes/IndexType.php | 21 +++ .../Attributes/MigrationAttribute.php | 66 +++++++ .../Migration/Attributes/ModifyColumn.php | 22 +++ .../Attributes/TableMigrationAttribute.php | 46 +++++ .../Exceptions/AttributeException.php | 17 ++ 20 files changed, 784 insertions(+) create mode 100644 core/Command/Db/Migrations/GenerateMetadataCommand.php create mode 100644 core/Command/Db/Migrations/PreviewCommand.php create mode 100644 lib/public/Migration/Attributes/AddColumn.php create mode 100644 lib/public/Migration/Attributes/AddIndex.php create mode 100644 lib/public/Migration/Attributes/ColumnMigrationAttribute.php create mode 100644 lib/public/Migration/Attributes/ColumnType.php create mode 100644 lib/public/Migration/Attributes/CreateTable.php create mode 100644 lib/public/Migration/Attributes/DeleteTable.php create mode 100644 lib/public/Migration/Attributes/DropColumn.php create mode 100644 lib/public/Migration/Attributes/DropIndex.php create mode 100644 lib/public/Migration/Attributes/GenericMigrationAttribute.php create mode 100644 lib/public/Migration/Attributes/IndexMigrationAttribute.php create mode 100644 lib/public/Migration/Attributes/IndexType.php create mode 100644 lib/public/Migration/Attributes/MigrationAttribute.php create mode 100644 lib/public/Migration/Attributes/ModifyColumn.php create mode 100644 lib/public/Migration/Attributes/TableMigrationAttribute.php create mode 100644 lib/public/Migration/Exceptions/AttributeException.php diff --git a/core/Command/Db/Migrations/GenerateMetadataCommand.php b/core/Command/Db/Migrations/GenerateMetadataCommand.php new file mode 100644 index 0000000000000..addcb59e68b03 --- /dev/null +++ b/core/Command/Db/Migrations/GenerateMetadataCommand.php @@ -0,0 +1,106 @@ +setName('migrations:generate-metadata') + ->setHidden(true) + ->setDescription('Generate metadata from DB migrations - internal and should not be used'); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output): int { + $output->writeln( + json_encode( + [ + 'migrations' => $this->extractMigrationMetadata() + ], + JSON_PRETTY_PRINT + ) + ); + + return 0; + } + + private function extractMigrationMetadata(): array { + return [ + 'core' => $this->extractMigrationMetadataFromCore(), + 'apps' => $this->extractMigrationMetadataFromApps() + ]; + } + + private function extractMigrationMetadataFromCore(): array { + return $this->extractMigrationAttributes('core'); + } + + /** + * get all apps and extract attributes + * + * @return array + * @throws \Exception + */ + private function extractMigrationMetadataFromApps(): array { + $allApps = \OC_App::getAllApps(); + $metadata = []; + foreach ($allApps as $appId) { + // We need to load app before being able to extract Migrations + // If app was not enabled before, we will disable it afterward. + $alreadyLoaded = $this->appManager->isInstalled($appId); + if (!$alreadyLoaded) { + $this->appManager->loadApp($appId); + } + $metadata[$appId] = $this->extractMigrationAttributes($appId); + if (!$alreadyLoaded) { + $this->appManager->disableApp($appId); + } + } + return $metadata; + } + + /** + * We get all migrations from an app, and for each migration we extract attributes + * + * @param string $appId + * + * @return array + * @throws \Exception + */ + private 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) { + $metadata[$version][] = $attribute->newInstance(); + } + } + + return $metadata; + } +} diff --git a/core/Command/Db/Migrations/PreviewCommand.php b/core/Command/Db/Migrations/PreviewCommand.php new file mode 100644 index 0000000000000..17d1d8b01ec61 --- /dev/null +++ b/core/Command/Db/Migrations/PreviewCommand.php @@ -0,0 +1,162 @@ +setName('migrations:preview') + ->setDescription('Get preview of available DB migrations in case of initiating an upgrade') + ->addArgument('version', InputArgument::REQUIRED, 'The destination version number'); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output): int { + $version = $input->getArgument('version'); + + $metadata = $this->getMetadata($version); + $parsed = $this->getMigrationsAttributes($metadata); + + $table = new Table($output); + $this->displayMigrations($table, 'core', $parsed['core']); + + $table->render(); + + return 0; + } + + private function displayMigrations(Table $table, string $appId, array $data): void { + $done = $this->getDoneMigrations($appId); + $done = array_diff($done, ['30000Date20240429122720']); + + $table->addRow( + [ + new TableCell( + $appId, + [ + 'colspan' => 2, + 'style' => new TableCellStyle(['cellFormat' => '%s']) + ] + ) + ] + )->addRow(new TableSeparator()); + + foreach($data as $migration => $attributes) { + if (in_array($migration, $done)) { + continue; + } + + $attributesStr = []; + /** @var MigrationAttribute[] $attributes */ + foreach($attributes as $attribute) { + $definition = '' . $attribute->definition() . ""; + $definition .= empty($attribute->getDescription()) ? '' : "\n " . $attribute->getDescription(); + $definition .= empty($attribute->getNotes()) ? '' : "\n " . implode("\n ", $attribute->getNotes()) . ''; + $attributesStr[] = $definition; + } + $table->addRow([$migration, implode("\n", $attributesStr)]); + } + + } + + + + + + private function getMetadata(string $version): array { + $metadata = json_decode(file_get_contents('/tmp/nextcloud-' . $version . '.metadata'), true); + if (!$metadata) { + throw new \Exception(); + } + return $metadata['migrations'] ?? []; + } + + private function getDoneMigrations(string $appId): array { + $ms = new MigrationService($appId, $this->connection); + return $ms->getMigratedVersions(); + } + + private function getMigrationsAttributes(array $metadata): array { + $appsAttributes = []; + foreach (array_keys($metadata['apps']) as $appId) { + $appsAttributes[$appId] = $this->parseMigrations($metadata['apps'][$appId] ?? []); + } + + return [ + 'core' => $this->parseMigrations($metadata['core'] ?? []), + 'apps' => $appsAttributes + ]; + } + + private function parseMigrations(array $migrations): array { + $parsed = []; + foreach (array_keys($migrations) as $entry) { + $items = $migrations[$entry]; + $parsed[$entry] = []; + foreach ($items 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; + } + + /** + * @param array $item + * + * @return MigrationAttribute|null + * @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(); + return $attribute->import($item); + } catch (\Error) { + throw new AttributeException('cannot import Attribute'); + } + } +} diff --git a/core/register_command.php b/core/register_command.php index 6e89568cf9bff..2922e98671a6c 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -67,6 +67,8 @@ $application->add(Server::get(Command\Db\ExpectedSchema::class)); $application->add(Server::get(Command\Db\ExportSchema::class)); + $application->add(Server::get(Command\Db\Migrations\GenerateMetadataCommand::class)); + $application->add(Server::get(Command\Db\Migrations\PreviewCommand::class)); if ($config->getSystemValueBool('debug', false)) { $application->add(Server::get(Command\Db\Migrations\StatusCommand::class)); $application->add(Server::get(Command\Db\Migrations\MigrateCommand::class)); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index dc0c8f0e6c6ec..a167eba878f46 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -572,6 +572,7 @@ 'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php', 'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php', +<<<<<<< HEAD 'OCP\\Mail\\Provider\\Address' => $baseDir . '/lib/public/Mail/Provider/Address.php', 'OCP\\Mail\\Provider\\Attachment' => $baseDir . '/lib/public/Mail/Provider/Attachment.php', 'OCP\\Mail\\Provider\\Exception\\Exception' => $baseDir . '/lib/public/Mail/Provider/Exception/Exception.php', @@ -584,7 +585,24 @@ 'OCP\\Mail\\Provider\\IProvider' => $baseDir . '/lib/public/Mail/Provider/IProvider.php', 'OCP\\Mail\\Provider\\IService' => $baseDir . '/lib/public/Mail/Provider/IService.php', 'OCP\\Mail\\Provider\\Message' => $baseDir . '/lib/public/Mail/Provider/Message.php', +======= + 'OCP\\Migration\\Attributes\\AddColumn' => $baseDir . '/lib/public/Migration/Attributes/AddColumn.php', + 'OCP\\Migration\\Attributes\\AddIndex' => $baseDir . '/lib/public/Migration/Attributes/AddIndex.php', + 'OCP\\Migration\\Attributes\\ColumnMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/ColumnMigrationAttribute.php', + 'OCP\\Migration\\Attributes\\ColumnType' => $baseDir . '/lib/public/Migration/Attributes/ColumnType.php', + 'OCP\\Migration\\Attributes\\CreateTable' => $baseDir . '/lib/public/Migration/Attributes/CreateTable.php', + 'OCP\\Migration\\Attributes\\DeleteTable' => $baseDir . '/lib/public/Migration/Attributes/DeleteTable.php', + 'OCP\\Migration\\Attributes\\DropColumn' => $baseDir . '/lib/public/Migration/Attributes/DropColumn.php', + 'OCP\\Migration\\Attributes\\DropIndex' => $baseDir . '/lib/public/Migration/Attributes/DropIndex.php', + 'OCP\\Migration\\Attributes\\GenericMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/GenericMigrationAttribute.php', + 'OCP\\Migration\\Attributes\\IndexMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/IndexMigrationAttribute.php', + 'OCP\\Migration\\Attributes\\IndexType' => $baseDir . '/lib/public/Migration/Attributes/IndexType.php', + 'OCP\\Migration\\Attributes\\MigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/MigrationAttribute.php', + 'OCP\\Migration\\Attributes\\ModifyColumn' => $baseDir . '/lib/public/Migration/Attributes/ModifyColumn.php', + 'OCP\\Migration\\Attributes\\TableMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/TableMigrationAttribute.php', +>>>>>>> 2f771df35a9 (feat(upgrade): migration attributes) 'OCP\\Migration\\BigIntMigration' => $baseDir . '/lib/public/Migration/BigIntMigration.php', + 'OCP\\Migration\\Exceptions\\AttributeException' => $baseDir . '/lib/public/Migration/Exceptions/AttributeException.php', 'OCP\\Migration\\IMigrationStep' => $baseDir . '/lib/public/Migration/IMigrationStep.php', 'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php', 'OCP\\Migration\\IRepairStep' => $baseDir . '/lib/public/Migration/IRepairStep.php', @@ -1143,7 +1161,9 @@ 'OC\\Core\\Command\\Db\\ExportSchema' => $baseDir . '/core/Command/Db/ExportSchema.php', 'OC\\Core\\Command\\Db\\Migrations\\ExecuteCommand' => $baseDir . '/core/Command/Db/Migrations/ExecuteCommand.php', 'OC\\Core\\Command\\Db\\Migrations\\GenerateCommand' => $baseDir . '/core/Command/Db/Migrations/GenerateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\GenerateMetadataCommand' => $baseDir . '/core/Command/Db/Migrations/GenerateMetadataCommand.php', 'OC\\Core\\Command\\Db\\Migrations\\MigrateCommand' => $baseDir . '/core/Command/Db/Migrations/MigrateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\PreviewCommand' => $baseDir . '/core/Command/Db/Migrations/PreviewCommand.php', 'OC\\Core\\Command\\Db\\Migrations\\StatusCommand' => $baseDir . '/core/Command/Db/Migrations/StatusCommand.php', 'OC\\Core\\Command\\Db\\SchemaEncoder' => $baseDir . '/core/Command/Db/SchemaEncoder.php', 'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => $baseDir . '/core/Command/Encryption/ChangeKeyStorageRoot.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 635854db4c6e4..b328d6ff5c996 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -605,6 +605,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php', 'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php', +<<<<<<< HEAD 'OCP\\Mail\\Provider\\Address' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Address.php', 'OCP\\Mail\\Provider\\Attachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Attachment.php', 'OCP\\Mail\\Provider\\Exception\\Exception' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/Exception.php', @@ -617,7 +618,24 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Mail\\Provider\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IProvider.php', 'OCP\\Mail\\Provider\\IService' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IService.php', 'OCP\\Mail\\Provider\\Message' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Message.php', +======= + 'OCP\\Migration\\Attributes\\AddColumn' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/AddColumn.php', + 'OCP\\Migration\\Attributes\\AddIndex' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/AddIndex.php', + 'OCP\\Migration\\Attributes\\ColumnMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/ColumnMigrationAttribute.php', + 'OCP\\Migration\\Attributes\\ColumnType' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/ColumnType.php', + 'OCP\\Migration\\Attributes\\CreateTable' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/CreateTable.php', + 'OCP\\Migration\\Attributes\\DeleteTable' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DeleteTable.php', + 'OCP\\Migration\\Attributes\\DropColumn' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DropColumn.php', + 'OCP\\Migration\\Attributes\\DropIndex' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DropIndex.php', + 'OCP\\Migration\\Attributes\\GenericMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/GenericMigrationAttribute.php', + 'OCP\\Migration\\Attributes\\IndexMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/IndexMigrationAttribute.php', + 'OCP\\Migration\\Attributes\\IndexType' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/IndexType.php', + 'OCP\\Migration\\Attributes\\MigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/MigrationAttribute.php', + 'OCP\\Migration\\Attributes\\ModifyColumn' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/ModifyColumn.php', + 'OCP\\Migration\\Attributes\\TableMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/TableMigrationAttribute.php', +>>>>>>> 2f771df35a9 (feat(upgrade): migration attributes) 'OCP\\Migration\\BigIntMigration' => __DIR__ . '/../../..' . '/lib/public/Migration/BigIntMigration.php', + 'OCP\\Migration\\Exceptions\\AttributeException' => __DIR__ . '/../../..' . '/lib/public/Migration/Exceptions/AttributeException.php', 'OCP\\Migration\\IMigrationStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IMigrationStep.php', 'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php', 'OCP\\Migration\\IRepairStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IRepairStep.php', @@ -1176,7 +1194,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Command\\Db\\ExportSchema' => __DIR__ . '/../../..' . '/core/Command/Db/ExportSchema.php', 'OC\\Core\\Command\\Db\\Migrations\\ExecuteCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/ExecuteCommand.php', 'OC\\Core\\Command\\Db\\Migrations\\GenerateCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/GenerateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\GenerateMetadataCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/GenerateMetadataCommand.php', 'OC\\Core\\Command\\Db\\Migrations\\MigrateCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/MigrateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\PreviewCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/PreviewCommand.php', 'OC\\Core\\Command\\Db\\Migrations\\StatusCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/StatusCommand.php', 'OC\\Core\\Command\\Db\\SchemaEncoder' => __DIR__ . '/../../..' . '/core/Command/Db/SchemaEncoder.php', 'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => __DIR__ . '/../../..' . '/core/Command/Encryption/ChangeKeyStorageRoot.php', diff --git a/lib/public/Migration/Attributes/AddColumn.php b/lib/public/Migration/Attributes/AddColumn.php new file mode 100644 index 0000000000000..aadf3263b31ea --- /dev/null +++ b/lib/public/Migration/Attributes/AddColumn.php @@ -0,0 +1,22 @@ +getType()) ? '' : ' (' . $this->getType()->value . ')'; + $table = empty($this->getTable()) ? '' : ' to table \'' . $this->getTable() . '\''; + return empty($this->getName()) ? + 'Addition of a new column' . $type . $table + : 'Addition of column \'' . $this->getName() . '\'' . $type . $table; + } +} diff --git a/lib/public/Migration/Attributes/AddIndex.php b/lib/public/Migration/Attributes/AddIndex.php new file mode 100644 index 0000000000000..9c96cd49a12af --- /dev/null +++ b/lib/public/Migration/Attributes/AddIndex.php @@ -0,0 +1,20 @@ +getType()) ? '' : ' (' . $this->getType()->value . ')'; + $table = empty($this->getTable()) ? '' : ' to table \'' . $this->getTable() . '\''; + return 'Addition of a new index' . $type . $table; + } +} diff --git a/lib/public/Migration/Attributes/ColumnMigrationAttribute.php b/lib/public/Migration/Attributes/ColumnMigrationAttribute.php new file mode 100644 index 0000000000000..6862f73e11dcc --- /dev/null +++ b/lib/public/Migration/Attributes/ColumnMigrationAttribute.php @@ -0,0 +1,58 @@ +name = $name; + return $this; + } + + public function getName(): string { + return $this->name; + } + + public function setType(?ColumnType $type): self { + $this->type = $type; + return $this; + } + + public function getType(): ?ColumnType { + return $this->type; + } + + public function import(array $data): self { + parent::import($data); + $this->setName($data['name'] ?? ''); + $this->setType(ColumnType::tryFrom($data['type'] ?? '')); + return $this; + } + + public function jsonSerialize(): array { + return array_merge( + parent::jsonSerialize(), + [ + 'name' => $this->getName(), + 'type' => $this->getType() ?? '', + ] + ); + } +} diff --git a/lib/public/Migration/Attributes/ColumnType.php b/lib/public/Migration/Attributes/ColumnType.php new file mode 100644 index 0000000000000..058b74f651686 --- /dev/null +++ b/lib/public/Migration/Attributes/ColumnType.php @@ -0,0 +1,46 @@ +getTable()) ? 'Creation of a new table' : 'Creation of new table \'' . $this->getTable() . '\''; + $definition .= empty($this->getColumns()) ? '' : ' with columns ' . implode(', ', $this->getColumns()); + return $definition; + } +} diff --git a/lib/public/Migration/Attributes/DeleteTable.php b/lib/public/Migration/Attributes/DeleteTable.php new file mode 100644 index 0000000000000..97bd36e385e05 --- /dev/null +++ b/lib/public/Migration/Attributes/DeleteTable.php @@ -0,0 +1,18 @@ +getTable()) ? 'Deletion of a table' : 'Deletion of table \'' . $this->getTable() . '\''; + } +} diff --git a/lib/public/Migration/Attributes/DropColumn.php b/lib/public/Migration/Attributes/DropColumn.php new file mode 100644 index 0000000000000..dfa1e81b315f5 --- /dev/null +++ b/lib/public/Migration/Attributes/DropColumn.php @@ -0,0 +1,21 @@ +getTable()) ? '' : ' from table \'' . $this->getTable() . '\''; + return empty($this->getName()) ? + 'Deletion of a column' . $table + : 'Deletion of column \'' . $this->getName() . '\'' . $table; + } +} diff --git a/lib/public/Migration/Attributes/DropIndex.php b/lib/public/Migration/Attributes/DropIndex.php new file mode 100644 index 0000000000000..cbeea4f5997a2 --- /dev/null +++ b/lib/public/Migration/Attributes/DropIndex.php @@ -0,0 +1,20 @@ +getTable()) ? + 'Deletion of an index' + : 'Deletion of an index from table \'' . $this->getTable() . '\''; + } +} diff --git a/lib/public/Migration/Attributes/GenericMigrationAttribute.php b/lib/public/Migration/Attributes/GenericMigrationAttribute.php new file mode 100644 index 0000000000000..f0e3af9761504 --- /dev/null +++ b/lib/public/Migration/Attributes/GenericMigrationAttribute.php @@ -0,0 +1,31 @@ +jsonSerialize(), JSON_UNESCAPED_SLASHES); + } + + public function jsonSerialize(): array { + return $this->details; + } +} diff --git a/lib/public/Migration/Attributes/IndexMigrationAttribute.php b/lib/public/Migration/Attributes/IndexMigrationAttribute.php new file mode 100644 index 0000000000000..fbe636bd6b0be --- /dev/null +++ b/lib/public/Migration/Attributes/IndexMigrationAttribute.php @@ -0,0 +1,46 @@ +type = $type; + return $this; + } + + public function getType(): ?IndexType { + return $this->type; + } + + public function import(array $data): self { + parent::import($data); + $this->setType(IndexType::tryFrom($data['type'] ?? '')); + return $this; + } + + public function jsonSerialize(): array { + return array_merge( + parent::jsonSerialize(), + [ + 'type' => $this->getType() ?? '', + ] + ); + } +} diff --git a/lib/public/Migration/Attributes/IndexType.php b/lib/public/Migration/Attributes/IndexType.php new file mode 100644 index 0000000000000..842f135e700f3 --- /dev/null +++ b/lib/public/Migration/Attributes/IndexType.php @@ -0,0 +1,21 @@ +table = $table; + return $this; + } + + public function getTable(): string { + return $this->table; + } + + public function setDescription(string $description): self { + $this->description = $description; + return $this; + } + + public function getDescription(): string { + return $this->description; + } + + public function setNotes(array $notes): self { + $this->notes = $notes; + return $this; + } + + public function getNotes(): array { + return $this->notes; + } + + public function definition(): string { + return json_encode($this->jsonSerialize(), JSON_UNESCAPED_SLASHES); + } + + public function import(array $data): self { + return $this->setTable($data['table'] ?? '') + ->setDescription($data['description'] ?? '') + ->setNotes($data['notes'] ?? []); + } + + public function jsonSerialize(): array { + return [ + 'class' => get_class($this), + 'table' => $this->getTable(), + 'description' => $this->getDescription(), + 'notes' => $this->getNotes(), + ]; + } +} diff --git a/lib/public/Migration/Attributes/ModifyColumn.php b/lib/public/Migration/Attributes/ModifyColumn.php new file mode 100644 index 0000000000000..e73b10478f76a --- /dev/null +++ b/lib/public/Migration/Attributes/ModifyColumn.php @@ -0,0 +1,22 @@ +getType()) ? '' : ' to ' . $this->getType()->value; + $table = empty($this->getTable()) ? '' : ' from table \'' . $this->getTable() . '\''; + return empty($this->getName()) ? + 'Modification of a column' . $table . $type + : 'Modification of column \'' . $this->getName() . '\'' . $table . $type; + } +} diff --git a/lib/public/Migration/Attributes/TableMigrationAttribute.php b/lib/public/Migration/Attributes/TableMigrationAttribute.php new file mode 100644 index 0000000000000..1c3f90c6acfd3 --- /dev/null +++ b/lib/public/Migration/Attributes/TableMigrationAttribute.php @@ -0,0 +1,46 @@ +columns = $columns; + return $this; + } + + public function getColumns(): array { + return $this->columns; + } + + public function import(array $data): self { + parent::import($data); + $this->setColumns($data['columns'] ?? []); + return $this; + } + + public function jsonSerialize(): array { + return array_merge( + parent::jsonSerialize(), + [ + 'columns' => $this->getColumns(), + ] + ); + } +} diff --git a/lib/public/Migration/Exceptions/AttributeException.php b/lib/public/Migration/Exceptions/AttributeException.php new file mode 100644 index 0000000000000..92de70f3e9651 --- /dev/null +++ b/lib/public/Migration/Exceptions/AttributeException.php @@ -0,0 +1,17 @@ + Date: Thu, 18 Jul 2024 10:35:48 -0100 Subject: [PATCH 2/5] feat(upgrade): release metadata Signed-off-by: Maxence Lange --- .../Db/Migrations/GenerateMetadataCommand.php | 40 +---- core/Command/Db/Migrations/PreviewCommand.php | 121 ++++---------- lib/private/Migration/MetadataManager.php | 153 ++++++++++++++++++ .../Exceptions/ReleaseMetadataException.php | 17 ++ lib/private/Updater/ReleaseMetadata.php | 80 +++++++++ 5 files changed, 287 insertions(+), 124 deletions(-) create mode 100644 lib/private/Migration/MetadataManager.php create mode 100644 lib/private/Updater/Exceptions/ReleaseMetadataException.php create mode 100644 lib/private/Updater/ReleaseMetadata.php diff --git a/core/Command/Db/Migrations/GenerateMetadataCommand.php b/core/Command/Db/Migrations/GenerateMetadataCommand.php index addcb59e68b03..64840bea23067 100644 --- a/core/Command/Db/Migrations/GenerateMetadataCommand.php +++ b/core/Command/Db/Migrations/GenerateMetadataCommand.php @@ -8,23 +8,21 @@ */ namespace OC\Core\Command\Db\Migrations; -use OC\DB\Connection; -use OC\DB\MigrationService; +use OC\Migration\MetadataManager; use OCP\App\IAppManager; -use ReflectionClass; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class GenerateMetadataCommand extends Command { public function __construct( - private readonly Connection $connection, + private readonly MetadataManager $metadataManager, private readonly IAppManager $appManager, ) { parent::__construct(); } - protected function configure() { + protected function configure(): void { $this->setName('migrations:generate-metadata') ->setHidden(true) ->setDescription('Generate metadata from DB migrations - internal and should not be used'); @@ -45,7 +43,9 @@ public function execute(InputInterface $input, OutputInterface $output): int { return 0; } - private function extractMigrationMetadata(): array { + + + public function extractMigrationMetadata(): array { return [ 'core' => $this->extractMigrationMetadataFromCore(), 'apps' => $this->extractMigrationMetadataFromApps() @@ -53,7 +53,7 @@ private function extractMigrationMetadata(): array { } private function extractMigrationMetadataFromCore(): array { - return $this->extractMigrationAttributes('core'); + return $this->metadataManager->extractMigrationAttributes('core'); } /** @@ -72,35 +72,11 @@ private function extractMigrationMetadataFromApps(): array { if (!$alreadyLoaded) { $this->appManager->loadApp($appId); } - $metadata[$appId] = $this->extractMigrationAttributes($appId); + $metadata[$appId] = $this->metadataManager->extractMigrationAttributes($appId); if (!$alreadyLoaded) { $this->appManager->disableApp($appId); } } return $metadata; } - - /** - * We get all migrations from an app, and for each migration we extract attributes - * - * @param string $appId - * - * @return array - * @throws \Exception - */ - private 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) { - $metadata[$version][] = $attribute->newInstance(); - } - } - - return $metadata; - } } diff --git a/core/Command/Db/Migrations/PreviewCommand.php b/core/Command/Db/Migrations/PreviewCommand.php index 17d1d8b01ec61..b1881a0c42c41 100644 --- a/core/Command/Db/Migrations/PreviewCommand.php +++ b/core/Command/Db/Migrations/PreviewCommand.php @@ -8,12 +8,9 @@ */ namespace OC\Core\Command\Db\Migrations; -use OC\DB\Connection; -use OC\DB\MigrationService; -use OCP\Migration\Attributes\GenericMigrationAttribute; +use OC\Migration\MetadataManager; +use OC\Updater\ReleaseMetadata; use OCP\Migration\Attributes\MigrationAttribute; -use OCP\Migration\Exceptions\AttributeException; -use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableCell; @@ -24,14 +21,15 @@ use Symfony\Component\Console\Output\OutputInterface; class PreviewCommand extends Command { + private bool $initiated = false; public function __construct( - private readonly Connection $connection, - private readonly LoggerInterface $logger, + private readonly MetadataManager $metadataManager, + private readonly ReleaseMetadata $releaseMetadata, ) { parent::__construct(); } - protected function configure() { + protected function configure(): void { $this ->setName('migrations:preview') ->setDescription('Get preview of available DB migrations in case of initiating an upgrade') @@ -42,21 +40,37 @@ protected function configure() { public function execute(InputInterface $input, OutputInterface $output): int { $version = $input->getArgument('version'); + if (filter_var($version, FILTER_VALIDATE_URL)) { + $metadata = $this->releaseMetadata->downloadMetadata($version); + } elseif (str_starts_with($version, '/')) { + $metadata = json_decode(file_get_contents($version), true, flags: JSON_THROW_ON_ERROR); + } else { + $metadata = $this->releaseMetadata->getMetadata($version); + } - $metadata = $this->getMetadata($version); - $parsed = $this->getMigrationsAttributes($metadata); + $parsed = $this->metadataManager->getMigrationsAttributesFromReleaseMetadata($metadata['migrations'] ?? [], true); $table = new Table($output); - $this->displayMigrations($table, 'core', $parsed['core']); - + $this->displayMigrations($table, 'core', $parsed['core'] ?? []); + foreach ($parsed['apps'] as $appId => $migrations) { + if (!empty($migrations)) { + $this->displayMigrations($table, $appId, $migrations); + } + } $table->render(); return 0; } private function displayMigrations(Table $table, string $appId, array $data): void { - $done = $this->getDoneMigrations($appId); - $done = array_diff($done, ['30000Date20240429122720']); + if (empty($data)) { + return; + } + + if ($this->initiated) { + $table->addRow(new TableSeparator()); + } + $this->initiated = true; $table->addRow( [ @@ -70,13 +84,9 @@ private function displayMigrations(Table $table, string $appId, array $data): vo ] )->addRow(new TableSeparator()); + /** @var MigrationAttribute[] $attributes */ foreach($data as $migration => $attributes) { - if (in_array($migration, $done)) { - continue; - } - $attributesStr = []; - /** @var MigrationAttribute[] $attributes */ foreach($attributes as $attribute) { $definition = '' . $attribute->definition() . ""; $definition .= empty($attribute->getDescription()) ? '' : "\n " . $attribute->getDescription(); @@ -85,78 +95,5 @@ private function displayMigrations(Table $table, string $appId, array $data): vo } $table->addRow([$migration, implode("\n", $attributesStr)]); } - - } - - - - - - private function getMetadata(string $version): array { - $metadata = json_decode(file_get_contents('/tmp/nextcloud-' . $version . '.metadata'), true); - if (!$metadata) { - throw new \Exception(); - } - return $metadata['migrations'] ?? []; - } - - private function getDoneMigrations(string $appId): array { - $ms = new MigrationService($appId, $this->connection); - return $ms->getMigratedVersions(); - } - - private function getMigrationsAttributes(array $metadata): array { - $appsAttributes = []; - foreach (array_keys($metadata['apps']) as $appId) { - $appsAttributes[$appId] = $this->parseMigrations($metadata['apps'][$appId] ?? []); - } - - return [ - 'core' => $this->parseMigrations($metadata['core'] ?? []), - 'apps' => $appsAttributes - ]; - } - - private function parseMigrations(array $migrations): array { - $parsed = []; - foreach (array_keys($migrations) as $entry) { - $items = $migrations[$entry]; - $parsed[$entry] = []; - foreach ($items 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; - } - - /** - * @param array $item - * - * @return MigrationAttribute|null - * @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(); - return $attribute->import($item); - } catch (\Error) { - throw new AttributeException('cannot import Attribute'); - } } } diff --git a/lib/private/Migration/MetadataManager.php b/lib/private/Migration/MetadataManager.php new file mode 100644 index 0000000000000..c5061b6fe0c1c --- /dev/null +++ b/lib/private/Migration/MetadataManager.php @@ -0,0 +1,153 @@ +connection); + + $metadata = []; + foreach($ms->getAvailableVersions() as $version) { + $metadata[$version] = []; + $class = new ReflectionClass($ms->createInstance($version)); + $attributes = $class->getAttributes(); + foreach ($attributes as $attribute) { + $metadata[$version][] = $attribute->newInstance(); + } + } + + return $metadata; + } + + /** + * convert direct data from release metadata into a list of Migrations' Attribute + * + * @param array $metadata + * @param bool $filterKnownMigrations ignore metadata already done in local instance + * + * @return array + * @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 + ]; + } + + /** + * convert raw data to a list of MigrationAttribute + * + * @param array $migrations + * @param array $ignoreMigrations + * + * @return array + */ + 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(); + return $attribute->import($item); + } catch (\Error) { + throw new AttributeException('cannot import Attribute'); + } + } +} diff --git a/lib/private/Updater/Exceptions/ReleaseMetadataException.php b/lib/private/Updater/Exceptions/ReleaseMetadataException.php new file mode 100644 index 0000000000000..bc82e4e03df7a --- /dev/null +++ b/lib/private/Updater/Exceptions/ReleaseMetadataException.php @@ -0,0 +1,17 @@ +downloadMetadata($url); + } + + /** + * download Metadata from a link + * + * @param string $url + * + * @return array + * @throws ReleaseMetadataException + * @since 30.0.0 + */ + public function downloadMetadata(string $url): array { + $client = $this->clientService->newClient(); + try { + $response = $client->get($url, [ + 'timeout' => 10, + 'connect_timeout' => 10, + 'verify' => false, + ]); + } catch (Exception $e) { + throw new ReleaseMetadataException('could not reach metadata at ' . $url, previous: $e); + } + + try { + return json_decode($response->getBody(), true, flags: JSON_THROW_ON_ERROR); + } catch (JsonException) { + throw new ReleaseMetadataException('remote document is not valid'); + } + } +} From 7c1ee524be784bf54d4c09d1310c182593d8b2f2 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Fri, 26 Jul 2024 16:35:51 -0100 Subject: [PATCH 3/5] fix(migration-attributes): privatizing AttributeException Signed-off-by: Maxence Lange --- .../Db/Migrations/GenerateMetadataCommand.php | 7 +-- core/Command/Db/Migrations/PreviewCommand.php | 3 ++ lib/composer/composer/autoload_classmap.php | 10 ++-- lib/composer/composer/autoload_static.php | 10 ++-- .../Exceptions/AttributeException.php | 2 +- lib/private/Migration/MetadataManager.php | 8 ++-- lib/public/Migration/Attributes/AddColumn.php | 7 +++ lib/public/Migration/Attributes/AddIndex.php | 7 +++ .../Attributes/ColumnMigrationAttribute.php | 33 +++++++++++++ .../Migration/Attributes/CreateTable.php | 9 +++- .../Migration/Attributes/DropColumn.php | 7 +++ lib/public/Migration/Attributes/DropIndex.php | 7 +++ .../{DeleteTable.php => DropTable.php} | 11 ++++- .../Attributes/GenericMigrationAttribute.php | 11 +++++ .../Attributes/IndexMigrationAttribute.php | 23 +++++++++ lib/public/Migration/Attributes/IndexType.php | 6 +-- .../Attributes/MigrationAttribute.php | 47 +++++++++++++++++++ .../Migration/Attributes/ModifyColumn.php | 7 +++ .../Attributes/TableMigrationAttribute.php | 25 +++++++++- 19 files changed, 215 insertions(+), 25 deletions(-) rename lib/{public => private}/Migration/Exceptions/AttributeException.php (87%) rename lib/public/Migration/Attributes/{DeleteTable.php => DropTable.php} (65%) diff --git a/core/Command/Db/Migrations/GenerateMetadataCommand.php b/core/Command/Db/Migrations/GenerateMetadataCommand.php index 64840bea23067..55a2a6aedab91 100644 --- a/core/Command/Db/Migrations/GenerateMetadataCommand.php +++ b/core/Command/Db/Migrations/GenerateMetadataCommand.php @@ -14,6 +14,9 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +/** + * @since 30.0.0 + */ class GenerateMetadataCommand extends Command { public function __construct( private readonly MetadataManager $metadataManager, @@ -43,9 +46,7 @@ public function execute(InputInterface $input, OutputInterface $output): int { return 0; } - - - public function extractMigrationMetadata(): array { + private function extractMigrationMetadata(): array { return [ 'core' => $this->extractMigrationMetadataFromCore(), 'apps' => $this->extractMigrationMetadataFromApps() diff --git a/core/Command/Db/Migrations/PreviewCommand.php b/core/Command/Db/Migrations/PreviewCommand.php index b1881a0c42c41..e35100dacb2e6 100644 --- a/core/Command/Db/Migrations/PreviewCommand.php +++ b/core/Command/Db/Migrations/PreviewCommand.php @@ -20,6 +20,9 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +/** + * @since 30.0.0 + */ class PreviewCommand extends Command { private bool $initiated = false; public function __construct( diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index a167eba878f46..8a1cd5514d4d6 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -572,7 +572,6 @@ 'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php', 'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php', -<<<<<<< HEAD 'OCP\\Mail\\Provider\\Address' => $baseDir . '/lib/public/Mail/Provider/Address.php', 'OCP\\Mail\\Provider\\Attachment' => $baseDir . '/lib/public/Mail/Provider/Attachment.php', 'OCP\\Mail\\Provider\\Exception\\Exception' => $baseDir . '/lib/public/Mail/Provider/Exception/Exception.php', @@ -585,24 +584,21 @@ 'OCP\\Mail\\Provider\\IProvider' => $baseDir . '/lib/public/Mail/Provider/IProvider.php', 'OCP\\Mail\\Provider\\IService' => $baseDir . '/lib/public/Mail/Provider/IService.php', 'OCP\\Mail\\Provider\\Message' => $baseDir . '/lib/public/Mail/Provider/Message.php', -======= 'OCP\\Migration\\Attributes\\AddColumn' => $baseDir . '/lib/public/Migration/Attributes/AddColumn.php', 'OCP\\Migration\\Attributes\\AddIndex' => $baseDir . '/lib/public/Migration/Attributes/AddIndex.php', 'OCP\\Migration\\Attributes\\ColumnMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/ColumnMigrationAttribute.php', 'OCP\\Migration\\Attributes\\ColumnType' => $baseDir . '/lib/public/Migration/Attributes/ColumnType.php', 'OCP\\Migration\\Attributes\\CreateTable' => $baseDir . '/lib/public/Migration/Attributes/CreateTable.php', - 'OCP\\Migration\\Attributes\\DeleteTable' => $baseDir . '/lib/public/Migration/Attributes/DeleteTable.php', 'OCP\\Migration\\Attributes\\DropColumn' => $baseDir . '/lib/public/Migration/Attributes/DropColumn.php', 'OCP\\Migration\\Attributes\\DropIndex' => $baseDir . '/lib/public/Migration/Attributes/DropIndex.php', + 'OCP\\Migration\\Attributes\\DropTable' => $baseDir . '/lib/public/Migration/Attributes/DropTable.php', 'OCP\\Migration\\Attributes\\GenericMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/GenericMigrationAttribute.php', 'OCP\\Migration\\Attributes\\IndexMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/IndexMigrationAttribute.php', 'OCP\\Migration\\Attributes\\IndexType' => $baseDir . '/lib/public/Migration/Attributes/IndexType.php', 'OCP\\Migration\\Attributes\\MigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/MigrationAttribute.php', 'OCP\\Migration\\Attributes\\ModifyColumn' => $baseDir . '/lib/public/Migration/Attributes/ModifyColumn.php', 'OCP\\Migration\\Attributes\\TableMigrationAttribute' => $baseDir . '/lib/public/Migration/Attributes/TableMigrationAttribute.php', ->>>>>>> 2f771df35a9 (feat(upgrade): migration attributes) 'OCP\\Migration\\BigIntMigration' => $baseDir . '/lib/public/Migration/BigIntMigration.php', - 'OCP\\Migration\\Exceptions\\AttributeException' => $baseDir . '/lib/public/Migration/Exceptions/AttributeException.php', 'OCP\\Migration\\IMigrationStep' => $baseDir . '/lib/public/Migration/IMigrationStep.php', 'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php', 'OCP\\Migration\\IRepairStep' => $baseDir . '/lib/public/Migration/IRepairStep.php', @@ -1672,6 +1668,8 @@ 'OC\\MemoryInfo' => $baseDir . '/lib/private/MemoryInfo.php', 'OC\\Migration\\BackgroundRepair' => $baseDir . '/lib/private/Migration/BackgroundRepair.php', 'OC\\Migration\\ConsoleOutput' => $baseDir . '/lib/private/Migration/ConsoleOutput.php', + 'OC\\Migration\\Exceptions\\AttributeException' => $baseDir . '/lib/private/Migration/Exceptions/AttributeException.php', + 'OC\\Migration\\MetadataManager' => $baseDir . '/lib/private/Migration/MetadataManager.php', 'OC\\Migration\\NullOutput' => $baseDir . '/lib/private/Migration/NullOutput.php', 'OC\\Migration\\SimpleOutput' => $baseDir . '/lib/private/Migration/SimpleOutput.php', 'OC\\NaturalSort' => $baseDir . '/lib/private/NaturalSort.php', @@ -1961,6 +1959,8 @@ 'OC\\Updater\\Changes' => $baseDir . '/lib/private/Updater/Changes.php', 'OC\\Updater\\ChangesCheck' => $baseDir . '/lib/private/Updater/ChangesCheck.php', 'OC\\Updater\\ChangesMapper' => $baseDir . '/lib/private/Updater/ChangesMapper.php', + 'OC\\Updater\\Exceptions\\ReleaseMetadataException' => $baseDir . '/lib/private/Updater/Exceptions/ReleaseMetadataException.php', + 'OC\\Updater\\ReleaseMetadata' => $baseDir . '/lib/private/Updater/ReleaseMetadata.php', 'OC\\Updater\\VersionCheck' => $baseDir . '/lib/private/Updater/VersionCheck.php', 'OC\\UserStatus\\ISettableProvider' => $baseDir . '/lib/private/UserStatus/ISettableProvider.php', 'OC\\UserStatus\\Manager' => $baseDir . '/lib/private/UserStatus/Manager.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index b328d6ff5c996..7c824b34160e7 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -605,7 +605,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php', 'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php', -<<<<<<< HEAD 'OCP\\Mail\\Provider\\Address' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Address.php', 'OCP\\Mail\\Provider\\Attachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Attachment.php', 'OCP\\Mail\\Provider\\Exception\\Exception' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/Exception.php', @@ -618,24 +617,21 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Mail\\Provider\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IProvider.php', 'OCP\\Mail\\Provider\\IService' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IService.php', 'OCP\\Mail\\Provider\\Message' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Message.php', -======= 'OCP\\Migration\\Attributes\\AddColumn' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/AddColumn.php', 'OCP\\Migration\\Attributes\\AddIndex' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/AddIndex.php', 'OCP\\Migration\\Attributes\\ColumnMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/ColumnMigrationAttribute.php', 'OCP\\Migration\\Attributes\\ColumnType' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/ColumnType.php', 'OCP\\Migration\\Attributes\\CreateTable' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/CreateTable.php', - 'OCP\\Migration\\Attributes\\DeleteTable' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DeleteTable.php', 'OCP\\Migration\\Attributes\\DropColumn' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DropColumn.php', 'OCP\\Migration\\Attributes\\DropIndex' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DropIndex.php', + 'OCP\\Migration\\Attributes\\DropTable' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/DropTable.php', 'OCP\\Migration\\Attributes\\GenericMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/GenericMigrationAttribute.php', 'OCP\\Migration\\Attributes\\IndexMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/IndexMigrationAttribute.php', 'OCP\\Migration\\Attributes\\IndexType' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/IndexType.php', 'OCP\\Migration\\Attributes\\MigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/MigrationAttribute.php', 'OCP\\Migration\\Attributes\\ModifyColumn' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/ModifyColumn.php', 'OCP\\Migration\\Attributes\\TableMigrationAttribute' => __DIR__ . '/../../..' . '/lib/public/Migration/Attributes/TableMigrationAttribute.php', ->>>>>>> 2f771df35a9 (feat(upgrade): migration attributes) 'OCP\\Migration\\BigIntMigration' => __DIR__ . '/../../..' . '/lib/public/Migration/BigIntMigration.php', - 'OCP\\Migration\\Exceptions\\AttributeException' => __DIR__ . '/../../..' . '/lib/public/Migration/Exceptions/AttributeException.php', 'OCP\\Migration\\IMigrationStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IMigrationStep.php', 'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php', 'OCP\\Migration\\IRepairStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IRepairStep.php', @@ -1705,6 +1701,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\MemoryInfo' => __DIR__ . '/../../..' . '/lib/private/MemoryInfo.php', 'OC\\Migration\\BackgroundRepair' => __DIR__ . '/../../..' . '/lib/private/Migration/BackgroundRepair.php', 'OC\\Migration\\ConsoleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/ConsoleOutput.php', + 'OC\\Migration\\Exceptions\\AttributeException' => __DIR__ . '/../../..' . '/lib/private/Migration/Exceptions/AttributeException.php', + 'OC\\Migration\\MetadataManager' => __DIR__ . '/../../..' . '/lib/private/Migration/MetadataManager.php', 'OC\\Migration\\NullOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/NullOutput.php', 'OC\\Migration\\SimpleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/SimpleOutput.php', 'OC\\NaturalSort' => __DIR__ . '/../../..' . '/lib/private/NaturalSort.php', @@ -1994,6 +1992,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Updater\\Changes' => __DIR__ . '/../../..' . '/lib/private/Updater/Changes.php', 'OC\\Updater\\ChangesCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/ChangesCheck.php', 'OC\\Updater\\ChangesMapper' => __DIR__ . '/../../..' . '/lib/private/Updater/ChangesMapper.php', + 'OC\\Updater\\Exceptions\\ReleaseMetadataException' => __DIR__ . '/../../..' . '/lib/private/Updater/Exceptions/ReleaseMetadataException.php', + 'OC\\Updater\\ReleaseMetadata' => __DIR__ . '/../../..' . '/lib/private/Updater/ReleaseMetadata.php', 'OC\\Updater\\VersionCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/VersionCheck.php', 'OC\\UserStatus\\ISettableProvider' => __DIR__ . '/../../..' . '/lib/private/UserStatus/ISettableProvider.php', 'OC\\UserStatus\\Manager' => __DIR__ . '/../../..' . '/lib/private/UserStatus/Manager.php', diff --git a/lib/public/Migration/Exceptions/AttributeException.php b/lib/private/Migration/Exceptions/AttributeException.php similarity index 87% rename from lib/public/Migration/Exceptions/AttributeException.php rename to lib/private/Migration/Exceptions/AttributeException.php index 92de70f3e9651..3daf99032ad5d 100644 --- a/lib/public/Migration/Exceptions/AttributeException.php +++ b/lib/private/Migration/Exceptions/AttributeException.php @@ -6,7 +6,7 @@ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCP\Migration\Exceptions; +namespace OC\Migration\Exceptions; use Exception; diff --git a/lib/private/Migration/MetadataManager.php b/lib/private/Migration/MetadataManager.php index c5061b6fe0c1c..2558d49f7f885 100644 --- a/lib/private/Migration/MetadataManager.php +++ b/lib/private/Migration/MetadataManager.php @@ -10,10 +10,10 @@ 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 OCP\Migration\Exceptions\AttributeException; use Psr\Log\LoggerInterface; use ReflectionClass; @@ -58,10 +58,10 @@ public function extractMigrationAttributes(string $appId): array { /** * convert direct data from release metadata into a list of Migrations' Attribute * - * @param array $metadata + * @param array> $metadata * @param bool $filterKnownMigrations ignore metadata already done in local instance * - * @return array + * @return array{apps: array>, core: array} * @since 30.0.0 */ public function getMigrationsAttributesFromReleaseMetadata( @@ -73,6 +73,7 @@ public function getMigrationsAttributesFromReleaseMetadata( 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); } @@ -126,7 +127,6 @@ private function getKnownMigrations(string $appId): array { return $ms->getMigratedVersions(); } - /** * generate (deserialize) a MigrationAttribute from a serialized version * diff --git a/lib/public/Migration/Attributes/AddColumn.php b/lib/public/Migration/Attributes/AddColumn.php index aadf3263b31ea..14a43c26f9950 100644 --- a/lib/public/Migration/Attributes/AddColumn.php +++ b/lib/public/Migration/Attributes/AddColumn.php @@ -10,8 +10,15 @@ use Attribute; +/** + * @since 30.0.0 + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class AddColumn extends ColumnMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { $type = is_null($this->getType()) ? '' : ' (' . $this->getType()->value . ')'; $table = empty($this->getTable()) ? '' : ' to table \'' . $this->getTable() . '\''; diff --git a/lib/public/Migration/Attributes/AddIndex.php b/lib/public/Migration/Attributes/AddIndex.php index 9c96cd49a12af..2a6f0628db162 100644 --- a/lib/public/Migration/Attributes/AddIndex.php +++ b/lib/public/Migration/Attributes/AddIndex.php @@ -10,8 +10,15 @@ use Attribute; +/** + * @since 30.0.0 + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class AddIndex extends IndexMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { $type = is_null($this->getType()) ? '' : ' (' . $this->getType()->value . ')'; $table = empty($this->getTable()) ? '' : ' to table \'' . $this->getTable() . '\''; diff --git a/lib/public/Migration/Attributes/ColumnMigrationAttribute.php b/lib/public/Migration/Attributes/ColumnMigrationAttribute.php index 6862f73e11dcc..dab636a3aaafa 100644 --- a/lib/public/Migration/Attributes/ColumnMigrationAttribute.php +++ b/lib/public/Migration/Attributes/ColumnMigrationAttribute.php @@ -10,6 +10,9 @@ use JsonSerializable; +/** + * @since 30.0.0 + */ class ColumnMigrationAttribute extends MigrationAttribute implements JsonSerializable { public function __construct( string $table = '', @@ -21,24 +24,50 @@ public function __construct( parent::__construct($table, $description, $notes); } + /** + * @param string $name + * + * @return $this + * @since 30.0.0 + */ public function setName(string $name): self { $this->name = $name; return $this; } + /** + * @return string + * @since 30.0.0 + */ public function getName(): string { return $this->name; } + /** + * @param ColumnType|null $type + * + * @return $this + * @since 30.0.0 + */ public function setType(?ColumnType $type): self { $this->type = $type; return $this; } + /** + * @return ColumnType|null + * @since 30.0.0 + */ public function getType(): ?ColumnType { return $this->type; } + /** + * @param array $data + * + * @return $this + * @since 30.0.0 + */ public function import(array $data): self { parent::import($data); $this->setName($data['name'] ?? ''); @@ -46,6 +75,10 @@ public function import(array $data): self { return $this; } + /** + * @return array + * @since 30.0.0 + */ public function jsonSerialize(): array { return array_merge( parent::jsonSerialize(), diff --git a/lib/public/Migration/Attributes/CreateTable.php b/lib/public/Migration/Attributes/CreateTable.php index 600d9bf4b9fff..8aac8cf247efb 100644 --- a/lib/public/Migration/Attributes/CreateTable.php +++ b/lib/public/Migration/Attributes/CreateTable.php @@ -10,10 +10,17 @@ use Attribute; +/** + * @since 30.0.0 + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class CreateTable extends TableMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { - $definition = empty($this->getTable()) ? 'Creation of a new table' : 'Creation of new table \'' . $this->getTable() . '\''; + $definition = 'Creation of new table \'' . $this->getTable() . '\''; $definition .= empty($this->getColumns()) ? '' : ' with columns ' . implode(', ', $this->getColumns()); return $definition; } diff --git a/lib/public/Migration/Attributes/DropColumn.php b/lib/public/Migration/Attributes/DropColumn.php index dfa1e81b315f5..0bb3efb191726 100644 --- a/lib/public/Migration/Attributes/DropColumn.php +++ b/lib/public/Migration/Attributes/DropColumn.php @@ -10,8 +10,15 @@ use Attribute; +/** + * @since 30.0.0 + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class DropColumn extends ColumnMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { $table = empty($this->getTable()) ? '' : ' from table \'' . $this->getTable() . '\''; return empty($this->getName()) ? diff --git a/lib/public/Migration/Attributes/DropIndex.php b/lib/public/Migration/Attributes/DropIndex.php index cbeea4f5997a2..0e72908ac353c 100644 --- a/lib/public/Migration/Attributes/DropIndex.php +++ b/lib/public/Migration/Attributes/DropIndex.php @@ -10,8 +10,15 @@ use Attribute; +/** + * @since 30.0.0 + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class DropIndex extends IndexMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { return empty($this->getTable()) ? 'Deletion of an index' diff --git a/lib/public/Migration/Attributes/DeleteTable.php b/lib/public/Migration/Attributes/DropTable.php similarity index 65% rename from lib/public/Migration/Attributes/DeleteTable.php rename to lib/public/Migration/Attributes/DropTable.php index 97bd36e385e05..5741af14108e1 100644 --- a/lib/public/Migration/Attributes/DeleteTable.php +++ b/lib/public/Migration/Attributes/DropTable.php @@ -10,9 +10,16 @@ use Attribute; +/** + * + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] -class DeleteTable extends MigrationAttribute { +class DropTable extends TableMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { - return empty($this->getTable()) ? 'Deletion of a table' : 'Deletion of table \'' . $this->getTable() . '\''; + return 'Deletion of table \'' . $this->getTable() . '\''; } } diff --git a/lib/public/Migration/Attributes/GenericMigrationAttribute.php b/lib/public/Migration/Attributes/GenericMigrationAttribute.php index f0e3af9761504..1f40d77d1f12a 100644 --- a/lib/public/Migration/Attributes/GenericMigrationAttribute.php +++ b/lib/public/Migration/Attributes/GenericMigrationAttribute.php @@ -10,6 +10,9 @@ use JsonSerializable; +/** + * @since 30.0.0 + */ class GenericMigrationAttribute extends MigrationAttribute implements JsonSerializable { public function __construct( private readonly array $details = [] @@ -21,10 +24,18 @@ public function __construct( ); } + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { return json_encode($this->jsonSerialize(), JSON_UNESCAPED_SLASHES); } + /** + * @return array + * @since 30.0.0 + */ public function jsonSerialize(): array { return $this->details; } diff --git a/lib/public/Migration/Attributes/IndexMigrationAttribute.php b/lib/public/Migration/Attributes/IndexMigrationAttribute.php index fbe636bd6b0be..33c5177c8ac35 100644 --- a/lib/public/Migration/Attributes/IndexMigrationAttribute.php +++ b/lib/public/Migration/Attributes/IndexMigrationAttribute.php @@ -10,6 +10,9 @@ use JsonSerializable; +/** + * @since 30.0.0 + */ class IndexMigrationAttribute extends MigrationAttribute implements JsonSerializable { public function __construct( string $table = '', @@ -20,21 +23,41 @@ public function __construct( parent::__construct($table, $description, $notes); } + /** + * @param IndexType|null $type + * + * @return $this + * @since 30.0.0 + */ public function setType(?IndexType $type): self { $this->type = $type; return $this; } + /** + * @return IndexType|null + * @since 30.0.0 + */ public function getType(): ?IndexType { return $this->type; } + /** + * @param array $data + * + * @return $this + * @since 30.0.0 + */ public function import(array $data): self { parent::import($data); $this->setType(IndexType::tryFrom($data['type'] ?? '')); return $this; } + /** + * @return array + * @since 30.0.0 + */ public function jsonSerialize(): array { return array_merge( parent::jsonSerialize(), diff --git a/lib/public/Migration/Attributes/IndexType.php b/lib/public/Migration/Attributes/IndexType.php index 842f135e700f3..b957aebafa72e 100644 --- a/lib/public/Migration/Attributes/IndexType.php +++ b/lib/public/Migration/Attributes/IndexType.php @@ -13,9 +13,9 @@ */ enum IndexType : string { /** @since 30.0.0 */ - case PRIMARY = 'primary'; // migration is estimated to require few minutes + case PRIMARY = 'primary'; /** @since 30.0.0 */ - case INDEX = 'index'; // depends on setup, migration might require some time + case INDEX = 'index'; /** @since 30.0.0 */ - case UNIQUE = 'unique'; // migration should be light and quick + case UNIQUE = 'unique'; } diff --git a/lib/public/Migration/Attributes/MigrationAttribute.php b/lib/public/Migration/Attributes/MigrationAttribute.php index 9f88614e37d1f..b9d698241d4d0 100644 --- a/lib/public/Migration/Attributes/MigrationAttribute.php +++ b/lib/public/Migration/Attributes/MigrationAttribute.php @@ -10,6 +10,9 @@ use JsonSerializable; +/** + * @since 30.0.0 + */ class MigrationAttribute implements JsonSerializable { public function __construct( private string $table = '', @@ -18,43 +21,87 @@ public function __construct( ) { } + /** + * @param string $table + * + * @return $this + * @since 30.0.0 + */ public function setTable(string $table): self { $this->table = $table; return $this; } + /** + * @return string + * @since 30.0.0 + */ public function getTable(): string { return $this->table; } + /** + * @param string $description + * + * @return $this + * @since 30.0.0 + */ public function setDescription(string $description): self { $this->description = $description; return $this; } + /** + * @return string + * @since 30.0.0 + */ public function getDescription(): string { return $this->description; } + /** + * @param array $notes + * + * @return $this + * @since 30.0.0 + */ public function setNotes(array $notes): self { $this->notes = $notes; return $this; } + /** + * @return array + * @since 30.0.0 + */ public function getNotes(): array { return $this->notes; } + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { return json_encode($this->jsonSerialize(), JSON_UNESCAPED_SLASHES); } + /** + * @param array $data + * + * @return self + * @since 30.0.0 + */ public function import(array $data): self { return $this->setTable($data['table'] ?? '') ->setDescription($data['description'] ?? '') ->setNotes($data['notes'] ?? []); } + /** + * @return array + * @since 30.0.0 + */ public function jsonSerialize(): array { return [ 'class' => get_class($this), diff --git a/lib/public/Migration/Attributes/ModifyColumn.php b/lib/public/Migration/Attributes/ModifyColumn.php index e73b10478f76a..216a911d90f3d 100644 --- a/lib/public/Migration/Attributes/ModifyColumn.php +++ b/lib/public/Migration/Attributes/ModifyColumn.php @@ -10,8 +10,15 @@ use Attribute; +/** + * @since 30.0.0 + */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class ModifyColumn extends ColumnMigrationAttribute { + /** + * @return string + * @since 30.0.0 + */ public function definition(): string { $type = is_null($this->getType()) ? '' : ' to ' . $this->getType()->value; $table = empty($this->getTable()) ? '' : ' from table \'' . $this->getTable() . '\''; diff --git a/lib/public/Migration/Attributes/TableMigrationAttribute.php b/lib/public/Migration/Attributes/TableMigrationAttribute.php index 1c3f90c6acfd3..571173b9ba182 100644 --- a/lib/public/Migration/Attributes/TableMigrationAttribute.php +++ b/lib/public/Migration/Attributes/TableMigrationAttribute.php @@ -10,9 +10,12 @@ use JsonSerializable; +/** + * @since 30.0.0 + */ class TableMigrationAttribute extends MigrationAttribute implements JsonSerializable { public function __construct( - string $table = '', + string $table, private array $columns = [], string $description = '', array $notes = [], @@ -20,21 +23,41 @@ public function __construct( parent::__construct($table, $description, $notes); } + /** + * @param array $columns + * + * @return $this + * @since 30.0.0 + */ public function setColumns(array $columns): self { $this->columns = $columns; return $this; } + /** + * @return array + * @since 30.0.0 + */ public function getColumns(): array { return $this->columns; } + /** + * @param array $data + * + * @return $this + * @since 30.0.0 + */ public function import(array $data): self { parent::import($data); $this->setColumns($data['columns'] ?? []); return $this; } + /** + * @return array + * @since 30.0.0 + */ public function jsonSerialize(): array { return array_merge( parent::jsonSerialize(), From ad490c963bd88359a714fb2f1786aaf8c00ae17c Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Mon, 29 Jul 2024 10:14:29 -0100 Subject: [PATCH 4/5] feat(migration-attributes): tests Signed-off-by: Maxence Lange --- .../composer/composer/autoload_classmap.php | 1 + .../composer/composer/autoload_static.php | 1 + .../Version30000Date20240102030405.php | 41 ++++ lib/private/Migration/MetadataManager.php | 9 +- lib/private/Updater/ReleaseMetadata.php | 3 +- lib/public/Migration/Attributes/AddColumn.php | 7 +- lib/public/Migration/Attributes/AddIndex.php | 5 +- .../Attributes/ColumnMigrationAttribute.php | 4 +- .../Migration/Attributes/ColumnType.php | 2 +- .../Migration/Attributes/CreateTable.php | 2 + .../Migration/Attributes/DropColumn.php | 7 +- lib/public/Migration/Attributes/DropIndex.php | 6 +- lib/public/Migration/Attributes/DropTable.php | 2 + .../Attributes/GenericMigrationAttribute.php | 3 + .../Attributes/IndexMigrationAttribute.php | 4 +- lib/public/Migration/Attributes/IndexType.php | 2 + .../Attributes/MigrationAttribute.php | 7 +- .../Migration/Attributes/ModifyColumn.php | 7 +- .../Attributes/TableMigrationAttribute.php | 2 + tests/lib/DB/MigrationsTest.php | 181 ++++++++++++++- tests/lib/Updater/ReleaseMetadataTest.php | 209 ++++++++++++++++++ 21 files changed, 475 insertions(+), 30 deletions(-) create mode 100644 apps/testing/lib/Migration/Version30000Date20240102030405.php create mode 100644 tests/lib/Updater/ReleaseMetadataTest.php diff --git a/apps/testing/composer/composer/autoload_classmap.php b/apps/testing/composer/composer/autoload_classmap.php index 079f887788100..6dce2e26361ba 100644 --- a/apps/testing/composer/composer/autoload_classmap.php +++ b/apps/testing/composer/composer/autoload_classmap.php @@ -16,6 +16,7 @@ 'OCA\\Testing\\Listener\\RegisterDeclarativeSettingsListener' => $baseDir . '/../lib/Listener/RegisterDeclarativeSettingsListener.php', 'OCA\\Testing\\Listener\\SetDeclarativeSettingsValueListener' => $baseDir . '/../lib/Listener/SetDeclarativeSettingsValueListener.php', 'OCA\\Testing\\Locking\\FakeDBLockingProvider' => $baseDir . '/../lib/Locking/FakeDBLockingProvider.php', + 'OCA\\Testing\\Migration\\Version30000Date20240102030405' => $baseDir . '/../lib/Migration/Version30000Date20240102030405.php', 'OCA\\Testing\\Provider\\FakeText2ImageProvider' => $baseDir . '/../lib/Provider/FakeText2ImageProvider.php', 'OCA\\Testing\\Provider\\FakeTextProcessingProvider' => $baseDir . '/../lib/Provider/FakeTextProcessingProvider.php', 'OCA\\Testing\\Provider\\FakeTextProcessingProviderSync' => $baseDir . '/../lib/Provider/FakeTextProcessingProviderSync.php', diff --git a/apps/testing/composer/composer/autoload_static.php b/apps/testing/composer/composer/autoload_static.php index 2332da70da97f..3be58e042894f 100644 --- a/apps/testing/composer/composer/autoload_static.php +++ b/apps/testing/composer/composer/autoload_static.php @@ -31,6 +31,7 @@ class ComposerStaticInitTesting 'OCA\\Testing\\Listener\\RegisterDeclarativeSettingsListener' => __DIR__ . '/..' . '/../lib/Listener/RegisterDeclarativeSettingsListener.php', 'OCA\\Testing\\Listener\\SetDeclarativeSettingsValueListener' => __DIR__ . '/..' . '/../lib/Listener/SetDeclarativeSettingsValueListener.php', 'OCA\\Testing\\Locking\\FakeDBLockingProvider' => __DIR__ . '/..' . '/../lib/Locking/FakeDBLockingProvider.php', + 'OCA\\Testing\\Migration\\Version30000Date20240102030405' => __DIR__ . '/..' . '/../lib/Migration/Version30000Date20240102030405.php', 'OCA\\Testing\\Provider\\FakeText2ImageProvider' => __DIR__ . '/..' . '/../lib/Provider/FakeText2ImageProvider.php', 'OCA\\Testing\\Provider\\FakeTextProcessingProvider' => __DIR__ . '/..' . '/../lib/Provider/FakeTextProcessingProvider.php', 'OCA\\Testing\\Provider\\FakeTextProcessingProviderSync' => __DIR__ . '/..' . '/../lib/Provider/FakeTextProcessingProviderSync.php', diff --git a/apps/testing/lib/Migration/Version30000Date20240102030405.php b/apps/testing/lib/Migration/Version30000Date20240102030405.php new file mode 100644 index 0000000000000..bacb187cb37a8 --- /dev/null +++ b/apps/testing/lib/Migration/Version30000Date20240102030405.php @@ -0,0 +1,41 @@ + * @since 30.0.0 */ public function extractMigrationAttributes(string $appId): array { @@ -48,7 +48,10 @@ public function extractMigrationAttributes(string $appId): array { $class = new ReflectionClass($ms->createInstance($version)); $attributes = $class->getAttributes(); foreach ($attributes as $attribute) { - $metadata[$version][] = $attribute->newInstance(); + $item = $attribute->newInstance(); + if ($item instanceof MigrationAttribute) { + $metadata[$version][] = $item; + } } } @@ -144,7 +147,7 @@ private function createAttribute(array $item): MigrationAttribute { } try { - $attribute = new $class(); + $attribute = new $class($item['table'] ?? ''); return $attribute->import($item); } catch (\Error) { throw new AttributeException('cannot import Attribute'); diff --git a/lib/private/Updater/ReleaseMetadata.php b/lib/private/Updater/ReleaseMetadata.php index 22d8c84341262..665847037e78c 100644 --- a/lib/private/Updater/ReleaseMetadata.php +++ b/lib/private/Updater/ReleaseMetadata.php @@ -64,8 +64,7 @@ public function downloadMetadata(string $url): array { try { $response = $client->get($url, [ 'timeout' => 10, - 'connect_timeout' => 10, - 'verify' => false, + 'connect_timeout' => 10 ]); } catch (Exception $e) { throw new ReleaseMetadataException('could not reach metadata at ' . $url, previous: $e); diff --git a/lib/public/Migration/Attributes/AddColumn.php b/lib/public/Migration/Attributes/AddColumn.php index 14a43c26f9950..8d16b9b6e8d9a 100644 --- a/lib/public/Migration/Attributes/AddColumn.php +++ b/lib/public/Migration/Attributes/AddColumn.php @@ -11,6 +11,8 @@ use Attribute; /** + * attribute on new column creation + * * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] @@ -21,9 +23,8 @@ class AddColumn extends ColumnMigrationAttribute { */ public function definition(): string { $type = is_null($this->getType()) ? '' : ' (' . $this->getType()->value . ')'; - $table = empty($this->getTable()) ? '' : ' to table \'' . $this->getTable() . '\''; return empty($this->getName()) ? - 'Addition of a new column' . $type . $table - : 'Addition of column \'' . $this->getName() . '\'' . $type . $table; + 'Addition of a new column' . $type . ' to table \'' . $this->getTable() . '\'' + : 'Addition of column \'' . $this->getName() . '\'' . $type . ' to table \'' . $this->getTable() . '\''; } } diff --git a/lib/public/Migration/Attributes/AddIndex.php b/lib/public/Migration/Attributes/AddIndex.php index 2a6f0628db162..ee22fe7f12898 100644 --- a/lib/public/Migration/Attributes/AddIndex.php +++ b/lib/public/Migration/Attributes/AddIndex.php @@ -11,6 +11,8 @@ use Attribute; /** + * attribute on index creation + * * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] @@ -21,7 +23,6 @@ class AddIndex extends IndexMigrationAttribute { */ public function definition(): string { $type = is_null($this->getType()) ? '' : ' (' . $this->getType()->value . ')'; - $table = empty($this->getTable()) ? '' : ' to table \'' . $this->getTable() . '\''; - return 'Addition of a new index' . $type . $table; + return 'Addition of a new index' . $type . ' to table \'' . $this->getTable() . '\''; } } diff --git a/lib/public/Migration/Attributes/ColumnMigrationAttribute.php b/lib/public/Migration/Attributes/ColumnMigrationAttribute.php index dab636a3aaafa..b750932a2578e 100644 --- a/lib/public/Migration/Attributes/ColumnMigrationAttribute.php +++ b/lib/public/Migration/Attributes/ColumnMigrationAttribute.php @@ -11,11 +11,13 @@ use JsonSerializable; /** + * generic class related to migration attribute about column changes + * * @since 30.0.0 */ class ColumnMigrationAttribute extends MigrationAttribute implements JsonSerializable { public function __construct( - string $table = '', + string $table, private string $name = '', private ?ColumnType $type = null, string $description = '', diff --git a/lib/public/Migration/Attributes/ColumnType.php b/lib/public/Migration/Attributes/ColumnType.php index 058b74f651686..23445e822b659 100644 --- a/lib/public/Migration/Attributes/ColumnType.php +++ b/lib/public/Migration/Attributes/ColumnType.php @@ -9,7 +9,7 @@ namespace OCP\Migration\Attributes; /** - * enum FieldType based on OCP\DB\Types + * enum ColumnType based on OCP\DB\Types * * @see \OCP\DB\Types * @since 30.0.0 diff --git a/lib/public/Migration/Attributes/CreateTable.php b/lib/public/Migration/Attributes/CreateTable.php index 8aac8cf247efb..df0418fa4bc2a 100644 --- a/lib/public/Migration/Attributes/CreateTable.php +++ b/lib/public/Migration/Attributes/CreateTable.php @@ -11,6 +11,8 @@ use Attribute; /** + * attribute on table creation + * * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] diff --git a/lib/public/Migration/Attributes/DropColumn.php b/lib/public/Migration/Attributes/DropColumn.php index 0bb3efb191726..1de0ba58489fb 100644 --- a/lib/public/Migration/Attributes/DropColumn.php +++ b/lib/public/Migration/Attributes/DropColumn.php @@ -11,6 +11,8 @@ use Attribute; /** + * attribute on column drop + * * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] @@ -20,9 +22,8 @@ class DropColumn extends ColumnMigrationAttribute { * @since 30.0.0 */ public function definition(): string { - $table = empty($this->getTable()) ? '' : ' from table \'' . $this->getTable() . '\''; return empty($this->getName()) ? - 'Deletion of a column' . $table - : 'Deletion of column \'' . $this->getName() . '\'' . $table; + 'Deletion of a column from table \'' . $this->getTable() . '\'' + : 'Deletion of column \'' . $this->getName() . '\' from table \'' . $this->getTable() . '\''; } } diff --git a/lib/public/Migration/Attributes/DropIndex.php b/lib/public/Migration/Attributes/DropIndex.php index 0e72908ac353c..2702cbed9a732 100644 --- a/lib/public/Migration/Attributes/DropIndex.php +++ b/lib/public/Migration/Attributes/DropIndex.php @@ -11,6 +11,8 @@ use Attribute; /** + * attribute on index drop + * * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] @@ -20,8 +22,6 @@ class DropIndex extends IndexMigrationAttribute { * @since 30.0.0 */ public function definition(): string { - return empty($this->getTable()) ? - 'Deletion of an index' - : 'Deletion of an index from table \'' . $this->getTable() . '\''; + return 'Deletion of an index from table \'' . $this->getTable() . '\''; } } diff --git a/lib/public/Migration/Attributes/DropTable.php b/lib/public/Migration/Attributes/DropTable.php index 5741af14108e1..e90e4804a3c9e 100644 --- a/lib/public/Migration/Attributes/DropTable.php +++ b/lib/public/Migration/Attributes/DropTable.php @@ -11,7 +11,9 @@ use Attribute; /** + * attribute on table drop * + * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] class DropTable extends TableMigrationAttribute { diff --git a/lib/public/Migration/Attributes/GenericMigrationAttribute.php b/lib/public/Migration/Attributes/GenericMigrationAttribute.php index 1f40d77d1f12a..d0c39a4a1a91a 100644 --- a/lib/public/Migration/Attributes/GenericMigrationAttribute.php +++ b/lib/public/Migration/Attributes/GenericMigrationAttribute.php @@ -11,6 +11,9 @@ use JsonSerializable; /** + * generic entry, used to replace migration attribute not yet known in current version + * but used in a future release + * * @since 30.0.0 */ class GenericMigrationAttribute extends MigrationAttribute implements JsonSerializable { diff --git a/lib/public/Migration/Attributes/IndexMigrationAttribute.php b/lib/public/Migration/Attributes/IndexMigrationAttribute.php index 33c5177c8ac35..0d6e946890e61 100644 --- a/lib/public/Migration/Attributes/IndexMigrationAttribute.php +++ b/lib/public/Migration/Attributes/IndexMigrationAttribute.php @@ -11,11 +11,13 @@ use JsonSerializable; /** + * generic class related to migration attribute about index changes + * * @since 30.0.0 */ class IndexMigrationAttribute extends MigrationAttribute implements JsonSerializable { public function __construct( - string $table = '', + string $table, private ?IndexType $type = null, string $description = '', array $notes = [], diff --git a/lib/public/Migration/Attributes/IndexType.php b/lib/public/Migration/Attributes/IndexType.php index b957aebafa72e..45c88d8104192 100644 --- a/lib/public/Migration/Attributes/IndexType.php +++ b/lib/public/Migration/Attributes/IndexType.php @@ -9,6 +9,8 @@ namespace OCP\Migration\Attributes; /** + * type of index + * * @since 30.0.0 */ enum IndexType : string { diff --git a/lib/public/Migration/Attributes/MigrationAttribute.php b/lib/public/Migration/Attributes/MigrationAttribute.php index b9d698241d4d0..19b2ffb56edae 100644 --- a/lib/public/Migration/Attributes/MigrationAttribute.php +++ b/lib/public/Migration/Attributes/MigrationAttribute.php @@ -15,7 +15,7 @@ */ class MigrationAttribute implements JsonSerializable { public function __construct( - private string $table = '', + private string $table, private string $description = '', private array $notes = [], ) { @@ -93,8 +93,7 @@ public function definition(): string { * @since 30.0.0 */ public function import(array $data): self { - return $this->setTable($data['table'] ?? '') - ->setDescription($data['description'] ?? '') + return $this->setDescription($data['description'] ?? '') ->setNotes($data['notes'] ?? []); } @@ -107,7 +106,7 @@ public function jsonSerialize(): array { 'class' => get_class($this), 'table' => $this->getTable(), 'description' => $this->getDescription(), - 'notes' => $this->getNotes(), + 'notes' => $this->getNotes() ]; } } diff --git a/lib/public/Migration/Attributes/ModifyColumn.php b/lib/public/Migration/Attributes/ModifyColumn.php index 216a911d90f3d..ef7250ffb343b 100644 --- a/lib/public/Migration/Attributes/ModifyColumn.php +++ b/lib/public/Migration/Attributes/ModifyColumn.php @@ -11,6 +11,8 @@ use Attribute; /** + * attribute on column modification + * * @since 30.0.0 */ #[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)] @@ -21,9 +23,8 @@ class ModifyColumn extends ColumnMigrationAttribute { */ public function definition(): string { $type = is_null($this->getType()) ? '' : ' to ' . $this->getType()->value; - $table = empty($this->getTable()) ? '' : ' from table \'' . $this->getTable() . '\''; return empty($this->getName()) ? - 'Modification of a column' . $table . $type - : 'Modification of column \'' . $this->getName() . '\'' . $table . $type; + 'Modification of a column from table \'' . $this->getTable() . '\'' . $type + : 'Modification of column \'' . $this->getName() . '\' from table \'' . $this->getTable() . '\'' . $type; } } diff --git a/lib/public/Migration/Attributes/TableMigrationAttribute.php b/lib/public/Migration/Attributes/TableMigrationAttribute.php index 571173b9ba182..f3ba406a4abfe 100644 --- a/lib/public/Migration/Attributes/TableMigrationAttribute.php +++ b/lib/public/Migration/Attributes/TableMigrationAttribute.php @@ -11,6 +11,8 @@ use JsonSerializable; /** + * generic class related to migration attribute about table changes + * * @since 30.0.0 */ class TableMigrationAttribute extends MigrationAttribute implements JsonSerializable { diff --git a/tests/lib/DB/MigrationsTest.php b/tests/lib/DB/MigrationsTest.php index 2bdd705ff5d45..a088ca1baf0d0 100644 --- a/tests/lib/DB/MigrationsTest.php +++ b/tests/lib/DB/MigrationsTest.php @@ -19,8 +19,21 @@ use OC\DB\Connection; use OC\DB\MigrationService; use OC\DB\SchemaWrapper; +use OC\Migration\MetadataManager; +use OCP\App\IAppManager; use OCP\IDBConnection; +use OCP\Migration\Attributes\AddColumn; +use OCP\Migration\Attributes\AddIndex; +use OCP\Migration\Attributes\ColumnType; +use OCP\Migration\Attributes\CreateTable; +use OCP\Migration\Attributes\DropColumn; +use OCP\Migration\Attributes\DropIndex; +use OCP\Migration\Attributes\DropTable; +use OCP\Migration\Attributes\IndexType; +use OCP\Migration\Attributes\ModifyColumn; use OCP\Migration\IMigrationStep; +use OCP\Server; +use PHPUnit\Framework\MockObject\MockObject; /** * Class MigrationsTest @@ -28,10 +41,9 @@ * @package Test\DB */ class MigrationsTest extends \Test\TestCase { - /** @var MigrationService | \PHPUnit\Framework\MockObject\MockObject */ - private $migrationService; - /** @var \PHPUnit\Framework\MockObject\MockObject | IDBConnection $db */ - private $db; + private MigrationService|MockObject $migrationService; + private MockObject|IDBConnection $db; + private IAppManager $appManager; protected function setUp(): void { parent::setUp(); @@ -39,6 +51,8 @@ protected function setUp(): void { $this->db = $this->createMock(Connection::class); $this->db->expects($this->any())->method('getPrefix')->willReturn('test_oc_'); $this->migrationService = new MigrationService('testing', $this->db); + + $this->appManager = Server::get(IAppManager::class); } public function testGetters() { @@ -755,4 +769,163 @@ public function testEnsureOracleConstraintsStringLength4000() { self::invokePrivate($this->migrationService, 'ensureOracleConstraints', [$sourceSchema, $schema, 3]); } + + + public function testExtractMigrationAttributes() { + $metadataManager = Server::get(MetadataManager::class); + $this->appManager->loadApp('testing'); + + $this->assertEquals($this->getMigrationMetadata(), json_decode(json_encode($metadataManager->extractMigrationAttributes('testing')), true)); + + $this->appManager->disableApp('testing'); + } + + public function testDeserializeMigrationMetadata() { + $metadataManager = Server::get(MetadataManager::class); + $this->assertEquals( + [ + 'core' => [], + 'apps' => [ + 'testing' => [ + '30000Date20240102030405' => [ + new DropTable('old_table'), + new CreateTable('new_table', + description: 'Table is used to store things, but also to get more things', + notes: ['this is a notice', 'and another one, if really needed'] + ), + new AddColumn('my_table'), + new AddColumn('my_table', 'another_field'), + new AddColumn('other_table', 'last_one', ColumnType::DATE), + new AddIndex('my_table'), + new AddIndex('my_table', IndexType::PRIMARY), + new DropColumn('other_table'), + new DropColumn('other_table', 'old_column', + description: 'field is not used anymore and replaced by \'last_one\'' + ), + new DropIndex('other_table'), + new ModifyColumn('other_table'), + new ModifyColumn('other_table', 'this_field'), + new ModifyColumn('other_table', 'this_field', ColumnType::BIGINT) + ] + ] + ] + ], + $metadataManager->getMigrationsAttributesFromReleaseMetadata( + [ + 'core' => [], + 'apps' => ['testing' => $this->getMigrationMetadata()] + ] + ) + ); + } + + private function getMigrationMetadata(): array { + return [ + '30000Date20240102030405' => [ + [ + 'class' => 'OCP\\Migration\\Attributes\\DropTable', + 'table' => 'old_table', + 'description' => '', + 'notes' => [], + 'columns' => [] + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\CreateTable', + 'table' => 'new_table', + 'description' => 'Table is used to store things, but also to get more things', + 'notes' => + [ + 'this is a notice', + 'and another one, if really needed' + ], + 'columns' => [] + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddColumn', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'name' => '', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddColumn', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'name' => 'another_field', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => 'last_one', + 'type' => 'date' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddIndex', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddIndex', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'type' => 'primary' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\DropColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => '', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\DropColumn', + 'table' => 'other_table', + 'description' => 'field is not used anymore and replaced by \'last_one\'', + 'notes' => [], + 'name' => 'old_column', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\DropIndex', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => '', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => 'this_field', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => 'this_field', + 'type' => 'bigint' + ], + ] + ]; + } } diff --git a/tests/lib/Updater/ReleaseMetadataTest.php b/tests/lib/Updater/ReleaseMetadataTest.php new file mode 100644 index 0000000000000..25fe39491522c --- /dev/null +++ b/tests/lib/Updater/ReleaseMetadataTest.php @@ -0,0 +1,209 @@ +clientService = $this->getMockBuilder(IClientService::class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testDownloadMetadata() { + $client = $this->createMock(IClient::class); + $response = $this->createMock(IResponse::class); + $this->clientService->expects($this->once()) + ->method('newClient') + ->with() + ->willReturn($client); + $client->expects($this->once()) + ->method('get') + ->willReturn($response); + $response->expects($this->once()) + ->method('getBody') + ->with() + ->willReturn($this->resultRequest()); + + + $releaseMetadata = new ReleaseMetadata($this->clientService); + $this->assertSame($this->resultRequestArray(), $releaseMetadata->downloadMetadata('ouila')); + } + + /** + * @dataProvider getMetadataUrlProvider + * + * @param string $version + * @param string $url + */ + public function testGetMetadata(string $version, string $url) { + $client = $this->createMock(IClient::class); + $response = $this->createMock(IResponse::class); + $this->clientService->expects($this->once()) + ->method('newClient') + ->with() + ->willReturn($client); + $client->expects($this->once()) + ->method('get') + ->with($url) + ->willReturn($response); + + $response->expects($this->once()) + ->method('getBody') + ->with() + ->willReturn('{}'); + + $releaseMetadata = new ReleaseMetadata($this->clientService); + $releaseMetadata->getMetadata($version); + } + + /** + * @return array + */ + public function getMetadataUrlProvider(): array { + return [ + [ + '30.0.0', + 'https://download.nextcloud.com/server/releases/nextcloud-30.0.0.metadata' + ], + [ + '30.0.0-beta1', + 'https://download.nextcloud.com/server/prereleases/nextcloud-30.0.0-beta1.metadata' + ], + [ + '30', + 'https://download.nextcloud.com/server/releases/latest-30.metadata' + ] + ]; + } + + private function resultRequest(): string { + return json_encode($this->resultRequestArray()); + } + + private function resultRequestArray(): array { + return [ + 'migrations' => [ + 'core' => [], + 'apps' => [ + 'testing' => [ + '30000Date20240102030405' => [ + 'class' => 'OCP\\Migration\\Attributes\\DropTable', + 'table' => 'old_table', + 'description' => '', + 'notes' => [], + 'columns' => [] + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\CreateTable', + 'table' => 'new_table', + 'description' => 'Table is used to store things, but also to get more things', + 'notes' => [ + 'this is a notice', + 'and another one, if really needed' + ], + 'columns' => [] + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddColumn', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'name' => '', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddColumn', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'name' => 'another_field', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => 'last_one', + 'type' => 'date' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddIndex', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\AddIndex', + 'table' => 'my_table', + 'description' => '', + 'notes' => [], + 'type' => 'primary' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\DropColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => '', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\DropColumn', + 'table' => 'other_table', + 'description' => 'field is not used anymore and replaced by \'last_one\'', + 'notes' => [], + 'name' => 'old_column', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\DropIndex', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => '', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => 'this_field', + 'type' => '' + ], + [ + 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn', + 'table' => 'other_table', + 'description' => '', + 'notes' => [], + 'name' => 'this_field', + 'type' => 'bigint' + ] + ] + ] + ] + ]; + } +} From a9e1dc668875f4247f6331096248b099afff5ae6 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Mon, 29 Jul 2024 11:16:38 -0100 Subject: [PATCH 5/5] fix(migration-attributes): psalm Signed-off-by: Maxence Lange --- .../Version30000Date20240102030405.php | 28 +++++++++---------- .../Attributes/ColumnMigrationAttribute.php | 8 ++++++ .../Attributes/GenericMigrationAttribute.php | 4 +++ .../Attributes/IndexMigrationAttribute.php | 7 +++++ .../Attributes/MigrationAttribute.php | 6 ++++ .../Attributes/TableMigrationAttribute.php | 7 +++++ 6 files changed, 46 insertions(+), 14 deletions(-) diff --git a/apps/testing/lib/Migration/Version30000Date20240102030405.php b/apps/testing/lib/Migration/Version30000Date20240102030405.php index bacb187cb37a8..e7b6bdcd61887 100644 --- a/apps/testing/lib/Migration/Version30000Date20240102030405.php +++ b/apps/testing/lib/Migration/Version30000Date20240102030405.php @@ -1,11 +1,11 @@