Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VACMS-136900: implement text field migration core logic #16227

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
services:
va_gov_live_field_migration.commands:
class: \Drupal\va_gov_live_field_migration\Commands\Commands
arguments: []
arguments:
- '@va_gov_live_field_migration.field_provider_resolver'
- '@va_gov_live_field_migration.migration_runner'
tags:
- { name: drush.command }
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Drupal\va_gov_live_field_migration\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
* Defines the annotation object for field provider plugins.
*
* @see plugin_api
* @see \Drupal\va_gov_live_field_migration\FieldProvider\Plugin\FieldProviderPluginInterface
* @see \Drupal\va_gov_live_field_migration\FieldProvider\Plugin\FieldProviderPluginManager
*
* @Annotation
*/
class FieldProvider extends Plugin {

/**
* The plugin ID.
*
* @var string
*/
public $id;

/**
* The human-readable name of the field provider plugin.
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Drupal\va_gov_live_field_migration\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
* Defines the annotation object for live field migration plugins.
*
* @see plugin_api
* @see \Drupal\va_gov_live_field_migration\Migration\Plugin\MigrationPluginInterface
* @see \Drupal\va_gov_live_field_migration\Migration\Plugin\MigrationPluginManager
*
* @Annotation
*/
class Migration extends Plugin {

/**
* The plugin ID.
*
* @var string
*/
public $id;

/**
* The human-readable name of the migration.
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Drupal\va_gov_live_field_migration\Commands;

use Drupal\va_gov_live_field_migration\FieldProvider\Resolver\ResolverInterface as FieldProviderResolverInterface;
use Drupal\va_gov_live_field_migration\Migration\Runner\RunnerInterface;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;

/**
Expand All @@ -10,121 +13,117 @@
class Commands extends DrushCommands {

/**
* Perform an operation, such as migrating, rolling back, or verifying.
* The field provider resolver service.
*
* @param callable $operation
* The operation to perform.
* @var \Drupal\va_gov_live_field_migration\FieldProvider\Resolver\ResolverInterface
*/
public function performOperation(callable $operation) {
$startTime = microtime(TRUE);
try {
$operation();
}
catch (\Exception $exception) {
$this->output()->writeln('Error: ' . $exception->getMessage());
}
finally {
$elapsedTime = microtime(TRUE) - $startTime;
$peakMemoryUsage = memory_get_peak_usage();
$this->output()->writeln('Elapsed time: ' . number_format($elapsedTime, 2) . ' seconds');
$this->output()->writeln('Peak memory usage: ' . number_format($peakMemoryUsage / 1024 / 1024, 2) . ' MB');
}
protected $fieldProviderResolver;

/**
* The migration runner service.
*
* @var \Drupal\va_gov_live_field_migration\Migration\Runner\RunnerInterface
*/
protected $migrationRunner;

/**
* Commands constructor.
*
* @param \Drupal\va_gov_live_field_migration\FieldProvider\Resolver\ResolverInterface $fieldProviderResolver
* The field provider resolver service.
* @param \Drupal\va_gov_live_field_migration\Migration\Runner\RunnerInterface $migrationRunner
* The migration runner service.
*/
public function __construct(
FieldProviderResolverInterface $fieldProviderResolver,
RunnerInterface $migrationRunner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [PHP_CodeSniffer] <Drupal.Functions.MultiLineFunctionDeclaration.MissingTrailingComma> reported by reviewdog 🐶
Multi-line function declarations must have a trailing comma after the last parameter

) {
$this->fieldProviderResolver = $fieldProviderResolver;
$this->migrationRunner = $migrationRunner;
}

/**
* Migrate a specific field on a specific content type.
* Migrate a specific field on a specific entity type.
*
* @param string $entityType
* The entity type.
* @param string $bundle
* The entity bundle or content type.
* @param string $fieldName
* The field name.
*
* @command va-gov-live-field-migration:migrate-field
* @aliases va-gov-live-field-migration-migrate-field
*/
public function migrateField(
#[CLI\Command(name: 'va-gov-live-field-migration:migrate', aliases: ['va-gov-live-field-migration-migrate'])]
#[CLI\Argument(name: 'entityType', description: 'The entity type')]
#[CLI\Argument(name: 'fieldName', description: 'The field name')]
public function migrate(
string $entityType,
string $bundle,
string $fieldName
) {
$this->performOperation(function () use ($entityType, $bundle, $fieldName) {
$this->output()->writeln('Migrating field ' . $fieldName . ' on ' . $entityType . ' ' . $bundle);
// Logic for the migration.
$this->output()->writeln('Migration successful.');
});
$migration = $this->migrationRunner->getMigration($entityType, $fieldName);
$this->migrationRunner->runMigration($migration, $entityType, $fieldName);
}

/**
* Rollback a specific field on a specific content type.
* Rollback a specific field on a specific entity type.
*
* @param string $entityType
* The entity type.
* @param string $bundle
* The entity bundle or content type.
* @param string $fieldName
* The field name.
*
* @command va-gov-live-field-migration:rollback-field
* @aliases va-gov-live-field-migration-rollback-field
*/
public function rollbackField(
#[CLI\Command(name: 'va-gov-live-field-migration:rollback', aliases: ['va-gov-live-field-migration-rollback'])]
#[CLI\Argument(name: 'entityType', description: 'The entity type')]
#[CLI\Argument(name: 'fieldName', description: 'The field name')]
public function rollback(
string $entityType,
string $bundle,
string $fieldName
) {
$this->performOperation(function () use ($entityType, $bundle, $fieldName) {
$this->output()->writeln('Rolling back field ' . $fieldName . ' on ' . $entityType . ' ' . $bundle);
// Logic for the rollback.
$this->output()->writeln('Rollback successful.');
});
$migration = $this->migrationRunner->getMigration($entityType, $fieldName);
$this->migrationRunner->rollbackMigration($migration, $entityType, $fieldName);
}

/**
* Verify a migration completed successfully.
*
* @param string $entityType
* The entity type.
* @param string $bundle
* The entity bundle or content type.
* @param string $fieldName
* The field name.
*
* @command va-gov-live-field-migration:verify
* @aliases va-gov-live-field-migration-verify
*/
#[CLI\Command(name: 'va-gov-live-field-migration:verify', aliases: ['va-gov-live-field-migration-verify'])]
#[CLI\Argument(name: 'entityType', description: 'The entity type')]
#[CLI\Argument(name: 'fieldName', description: 'The field name')]
public function verify(
string $entityType,
string $bundle,
string $fieldName
) {
$this->performOperation(function () use ($entityType, $bundle, $fieldName) {
$this->output()->writeln('Verifying field ' . $fieldName . ' on ' . $entityType . ' ' . $bundle);
// Logic for the verification.
$this->output()->writeln('Verification successful.');
});
$migration = $this->migrationRunner->getMigration($entityType, $fieldName);
$this->migrationRunner->verifyMigration($migration, $entityType, $fieldName);
}

/**
* Find fields that haven't been migrated yet.
*
* @param string $entityType
* The entity type.
* @param string $bundle
* The entity bundle or content type.
*
* @command va-gov-live-field-migration:find
* @aliases va-gov-live-field-migration-find
*/
public function find(
string $entityType,
string $bundle
) {
$this->performOperation(function () use ($entityType, $bundle) {
$this->output()->writeln('Finding fields on ' . $entityType . ' ' . $bundle);
// Logic for finding fields.
});
#[CLI\Command(name: 'va-gov-live-field-migration:find', aliases: ['va-gov-live-field-migration-find'])]
#[CLI\Option(name: 'field-provider', description: 'The field provider to use')]
#[CLI\Option(name: 'entity-type', description: 'The entity type to use')]
#[CLI\Option(name: 'bundle', description: 'The bundle to use')]
public function find($options = [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [PHP_CodeSniffer] <Drupal.Functions.MultiLineFunctionDeclaration.FirstParamSpacing> reported by reviewdog 🐶
The first parameter of a multi-line function declaration must be on the line after the opening bracket

// Default to the issue 14995 field provider.
// @see https://github.com/department-of-veterans-affairs/va-gov-cms/issues/14995
'field-provider' => 'issue_14995',
'entity-type' => 'node',
'bundle' => NULL,
]) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [PHP_CodeSniffer] <Drupal.Functions.MultiLineFunctionDeclaration.MissingTrailingComma> reported by reviewdog 🐶
Multi-line function declarations must have a trailing comma after the last parameter

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [PHP_CodeSniffer] <Drupal.Functions.MultiLineFunctionDeclaration.CloseBracketLine> reported by reviewdog 🐶
The closing parenthesis of a multi-line function declaration must be on a new line

$fieldProvider = $options['field-provider'];
$entityType = $options['entity-type'];
$bundle = $options['bundle'];
$this->output()->writeln('Finding fields with field provider "' . $fieldProvider . '" on entity type "' . $entityType . '", bundle "' . ($bundle ?: 'NULL') . '"...');
$fields = $this->fieldProviderResolver
->getFieldProvider($fieldProvider)
->getFields($entityType, $bundle);
$this->output()->writeln('Found ' . count($fields) . ' fields.');
foreach ($fields as $field) {
$this->output()->writeln($field);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace Drupal\va_gov_live_field_migration\Database;

use Drupal\Core\Database\Connection;

/**
* A database service to abstract database operations.
*/
class Database implements DatabaseInterface {

/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;

/**
* Constructor.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(
Connection $connection

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [PHP_CodeSniffer] <Drupal.Functions.MultiLineFunctionDeclaration.MissingTrailingComma> reported by reviewdog 🐶
Multi-line function declarations must have a trailing comma after the last parameter

) {
$this->connection = $connection;
}

/**
* {@inheritDoc}
*/
public function dropTable(string $table): void {
$this->connection->schema()->dropTable($table);
}

/**
* {@inheritDoc}
*/
public function getPrimaryFieldTableName(string $entityType, string $fieldName): string {
return "{$entityType}__{$fieldName}";
}

/**
* {@inheritDoc}
*/
public function getFieldRevisionTableName(string $entityType, string $fieldName): string {
return "{$entityType}__{$fieldName}";
}

/**
* {@inheritDoc}
*/
public function getBackupTableName(string $tableName): string {
// Table names are limited to 64 characters, but Drupal field tables can
// be longer than that. So we need to truncate the table name.
$tableName = substr($tableName, 0, 64 - strlen(self::BACKUP_TABLE_SUFFIX));
return "{$tableName}__backup";
}

/**
* {@inheritDoc}
*/
public function createTable(string $newTable, string $existingTable): void {
$this->connection->query("CREATE TABLE {$newTable} LIKE {$existingTable};");
}

/**
* {@inheritDoc}
*/
public function copyTable(string $sourceTable, string $destinationTable, bool $preserve = FALSE): void {
if (!$preserve) {
$this->dropTable($destinationTable);
$this->createTable($destinationTable, $sourceTable);
}
$this->connection->query("INSERT {$destinationTable} SELECT * FROM {$sourceTable};");
}

/**
* {@inheritDoc}
*/
public function backupPrimaryFieldTable(string $entityType, string $fieldName, bool $preserve = FALSE): void {
$primaryTable = $this->getPrimaryFieldTableName($entityType, $fieldName);
$backupTable = $this->getBackupTableName($primaryTable);
$this->copyTable($primaryTable, $backupTable, $preserve);
}

/**
* {@inheritDoc}
*/
public function backupFieldRevisionTable(string $entityType, string $fieldName, bool $preserve = FALSE): void {
$revisionTable = $this->getFieldRevisionTableName($entityType, $fieldName);
$backupTable = $this->getBackupTableName($revisionTable);
$this->copyTable($revisionTable, $backupTable, $preserve);
}

/**
* {@inheritDoc}
*/
public function restorePrimaryFieldTable(string $entityType, string $fieldName, bool $preserve = FALSE): void {
$primaryTable = $this->getPrimaryFieldTableName($entityType, $fieldName);
$backupTable = $this->getBackupTableName($primaryTable);
$this->copyTable($backupTable, $primaryTable, $preserve);
}

/**
* {@inheritDoc}
*/
public function restoreFieldRevisionTable(string $entityType, string $fieldName, bool $preserve = FALSE): void {
$revisionTable = $this->getFieldRevisionTableName($entityType, $fieldName);
$backupTable = $this->getBackupTableName($revisionTable);
$this->copyTable($backupTable, $revisionTable, $preserve);
}

}
Loading
Loading