From 6ed120059d321cae0fae3452fe08be607e0209f6 Mon Sep 17 00:00:00 2001 From: Nathan Douglas Date: Tue, 12 Sep 2023 08:24:14 -0400 Subject: [PATCH 1/5] VACMS-136900: Implement text-field migration core logic. --- .../drush.services.yml | 4 +- .../src/Annotation/FieldProvider.php | 32 ++++ .../src/Annotation/Migration.php | 32 ++++ .../src/Commands/Commands.php | 98 +++++++--- .../CouldNotCalculateStatusKeyException.php | 8 + .../FieldProviderNotFoundException.php | 8 + .../src/Exception/MigrationErrorException.php | 8 + .../Exception/MigrationNotFoundException.php | 8 + .../Exception/MigrationRollbackException.php | 8 + .../MigrationVerificationException.php | 8 + .../StatusDeserializationException.php | 8 + .../src/Exception/StatusNotFoundException.php | 8 + .../Plugin/FieldProviderPluginBase.php | 57 ++++++ .../Plugin/FieldProviderPluginInterface.php | 23 +++ .../Plugin/FieldProviderPluginManager.php | 50 +++++ .../FieldProviderPluginManagerInterface.php | 24 +++ .../src/FieldProvider/Resolver/Resolver.php | 38 ++++ .../Resolver/ResolverInterface.php | 23 +++ .../Migration/Plugin/MigrationPluginBase.php | 78 ++++++++ .../Plugin/MigrationPluginInterface.php | 60 ++++++ .../Plugin/MigrationPluginManager.php | 50 +++++ .../MigrationPluginManagerInterface.php | 24 +++ .../src/Migration/Resolver/Resolver.php | 82 +++++++++ .../Migration/Resolver/ResolverInterface.php | 28 +++ .../src/Migration/Status/Key/Key.php | 22 +++ .../src/Migration/Status/Key/KeyInterface.php | 26 +++ .../src/Migration/Status/Status.php | 173 ++++++++++++++++++ .../src/Migration/Status/StatusInterface.php | 84 +++++++++ .../Plugin/FieldProvider/TestEmptyList.php | 24 +++ .../Plugin/Migration/StringToStringLong.php | 41 +++++ .../src/Plugin/Migration/TestException.php | 43 +++++ .../src/Plugin/Migration/TestSuccess.php | 40 ++++ .../src/Plugin/Migration/TextToStringLong.php | 41 +++++ .../src/Reporter/Reporter.php | 66 +++++++ .../src/Reporter/ReporterInterface.php | 34 ++++ .../src/State/State.php | 60 ++++++ .../src/State/StateInterface.php | 50 +++++ .../va_gov_live_field_migration.services.yml | 22 +++ .../Plugin/FieldProviderPluginManagerTest.php | 30 +++ .../FieldProvider/Resolver/ResolverTest.php | 30 +++ .../Plugin/MigrationPluginManagerTest.php | 30 +++ .../Migration/Resolver/ResolverTest.php | 30 +++ .../functional/Reporter/ReporterTest.php | 30 +++ .../functional/State/StateTest.php | 63 +++++++ .../unit/Migration/Status/StatusTest.php | 89 +++++++++ 45 files changed, 1767 insertions(+), 28 deletions(-) create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Annotation/FieldProvider.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Annotation/Migration.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/CouldNotCalculateStatusKeyException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/FieldProviderNotFoundException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationErrorException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationNotFoundException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationRollbackException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationVerificationException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/StatusDeserializationException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/StatusNotFoundException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginBase.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginManager.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginManagerInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Resolver/Resolver.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Resolver/ResolverInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginBase.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginManager.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginManagerInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/Resolver.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/ResolverInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Key/Key.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Key/KeyInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Status.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/StatusInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/TestEmptyList.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/StringToStringLong.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TestException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TestSuccess.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Reporter/Reporter.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Reporter/ReporterInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/State/State.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/FieldProvider/Plugin/FieldProviderPluginManagerTest.php create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/FieldProvider/Resolver/ResolverTest.php create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/Migration/Plugin/MigrationPluginManagerTest.php create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/Migration/Resolver/ResolverTest.php create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/Reporter/ReporterTest.php create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/State/StateTest.php create mode 100644 tests/phpunit/va_gov_live_field_migration/unit/Migration/Status/StatusTest.php diff --git a/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml b/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml index e3f89e571a..5815781a69 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml +++ b/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml @@ -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_resolver' tags: - { name: drush.command } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Annotation/FieldProvider.php b/docroot/modules/custom/va_gov_live_field_migration/src/Annotation/FieldProvider.php new file mode 100644 index 0000000000..a2ab460fd4 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Annotation/FieldProvider.php @@ -0,0 +1,32 @@ +migrationResolver = $migrationResolver; + $this->fieldProviderResolver = $fieldProviderResolver; + } + /** * Perform an operation, such as migrating, rolling back, or verifying. * @@ -32,12 +64,10 @@ public function performOperation(callable $operation) { } /** - * 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. * @@ -46,23 +76,22 @@ public function performOperation(callable $operation) { */ public function migrateField( 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->performOperation(function () use ($entityType, $fieldName) { + $this->output()->writeln('Migrating field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $this->migrationResolver + ->getMigration($entityType, $fieldName) + ->runMigration($entityType, $fieldName); $this->output()->writeln('Migration successful.'); }); } /** - * 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. * @@ -71,12 +100,13 @@ public function migrateField( */ public function rollbackField( 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->performOperation(function () use ($entityType, $fieldName) { + $this->output()->writeln('Rolling back migration of field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $this->migrationResolver + ->getMigration($entityType, $fieldName) + ->rollbackMigration($entityType, $fieldName); $this->output()->writeln('Rollback successful.'); }); } @@ -86,8 +116,6 @@ public function rollbackField( * * @param string $entityType * The entity type. - * @param string $bundle - * The entity bundle or content type. * @param string $fieldName * The field name. * @@ -96,12 +124,13 @@ public function rollbackField( */ 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->performOperation(function () use ($entityType, $fieldName) { + $this->output()->writeln('Verifying migration of field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $this->migrationResolver + ->getMigration($entityType, $fieldName) + ->verifyMigration($entityType, $fieldName); $this->output()->writeln('Verification successful.'); }); } @@ -109,21 +138,36 @@ public function verify( /** * Find fields that haven't been migrated yet. * - * @param string $entityType + * @param string|null $fieldProvider + * The field provider. + * @param string|null $entityType * The entity type. - * @param string $bundle + * @param string|null $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 + string $fieldProvider = NULL, + string $entityType = NULL, + string $bundle = NULL ) { - $this->performOperation(function () use ($entityType, $bundle) { - $this->output()->writeln('Finding fields on ' . $entityType . ' ' . $bundle); - // Logic for finding fields. + if ($fieldProvider === NULL) { + $fieldProvider = 'test_empty_list'; + } + if ($entityType === NULL) { + $entityType = 'node'; + } + $this->performOperation(function () use ($fieldProvider, $entityType, $bundle) { + $this->output()->writeln('Finding fields 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); + } }); } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Exception/CouldNotCalculateStatusKeyException.php b/docroot/modules/custom/va_gov_live_field_migration/src/Exception/CouldNotCalculateStatusKeyException.php new file mode 100644 index 0000000000..575725bf6d --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Exception/CouldNotCalculateStatusKeyException.php @@ -0,0 +1,8 @@ +stringTranslation = $stringTranslation; + } + + /** + * {@inheritDoc} + * + * @codeCoverageIgnore + */ + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition + ) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('string_translation') + ); + } + + /** + * {@inheritDoc} + */ + abstract public function getFields(string $entityType, string $bundle = NULL): array; + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginInterface.php new file mode 100644 index 0000000000..b141dc29fb --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginInterface.php @@ -0,0 +1,23 @@ +alterInfo('va_gov_live_field_migration_field_provider_info'); + $this->setCacheBackend($cache_backend, 'va_gov_live_field_migration_field_provider'); + } + + /** + * {@inheritDoc} + */ + public function getFieldProvider(string $id) : FieldProviderPluginInterface { + try { + return $this->createInstance($id); + } + catch (PluginException) { + throw new FieldProviderNotFoundException("Unknown field provider: $id"); + } + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginManagerInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginManagerInterface.php new file mode 100644 index 0000000000..849e2fefac --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Plugin/FieldProviderPluginManagerInterface.php @@ -0,0 +1,24 @@ +pluginManager = $pluginManager; + } + + /** + * {@inheritDoc} + */ + public function getFieldProvider(string $id) : FieldProviderPluginInterface { + return $this->pluginManager->getFieldProvider($id); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Resolver/ResolverInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Resolver/ResolverInterface.php new file mode 100644 index 0000000000..e6c1d31af0 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/FieldProvider/Resolver/ResolverInterface.php @@ -0,0 +1,23 @@ +reporter = $reporter; + $this->stringTranslation = $stringTranslation; + } + + /** + * {@inheritDoc} + * + * @codeCoverageIgnore + */ + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition + ) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('va_gov_live_field_migration.reporter'), + $container->get('string_translation') + ); + } + + /** + * {@inheritDoc} + */ + abstract public function runMigration(string $entityType, string $fieldName); + + /** + * {@inheritDoc} + */ + abstract public function rollbackMigration(string $entityType, string $fieldName); + + /** + * {@inheritDoc} + */ + abstract public function verifyMigration(string $entityType, string $fieldName); + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginInterface.php new file mode 100644 index 0000000000..ed8a8c4297 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginInterface.php @@ -0,0 +1,60 @@ +alterInfo('va_gov_live_field_migration_migration_info'); + $this->setCacheBackend($cache_backend, 'va_gov_live_field_migration_migration'); + } + + /** + * {@inheritDoc} + */ + public function getMigration(string $id) : MigrationPluginInterface { + try { + return $this->createInstance($id); + } + catch (PluginException) { + throw new MigrationNotFoundException("Unknown migration: $id"); + } + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginManagerInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginManagerInterface.php new file mode 100644 index 0000000000..ab70ac5690 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Plugin/MigrationPluginManagerInterface.php @@ -0,0 +1,24 @@ +pluginManager = $pluginManager; + $this->entityFieldManager = $entityFieldManager; + } + + /** + * Calculate the migration plugin ID for a given field. + * + * We might someday use a more complex algorithm for this, but right now we: + * + * - Retrieve the field storage for the specified field (or assert). + * - If the field is of type 'string', then we use 'string_to_string_long'. + * - If the field is of type 'text_long', then we use 'text_to_string_long'. + * - Otherwise, we throw an exception. + * + * @param string $entityType + * The entity type. + * @param string $fieldName + * The field name. + * + * @return string + * The migration plugin ID. + * + * @throws \Drupal\va_gov_live_field_migration\Exception\MigrationNotFoundException + * If a suitable strategy cannot be found. + */ + public function getMigrationId(string $entityType, string $fieldName): string { + $fieldStorage = $this->entityFieldManager->getFieldStorageDefinitions($entityType)[$fieldName]; + if ($fieldStorage->getType() === 'string') { + return 'string_to_string_long'; + } + elseif ($fieldStorage->getType() === 'text_long') { + return 'text_to_string_long'; + } + throw new MigrationNotFoundException(sprintf('No migration found for field %s on entity type %s.', $fieldName, $entityType)); + } + + /** + * {@inheritDoc} + */ + public function getMigration(string $entityType, string $fieldName) : MigrationPluginInterface { + $id = $this->getMigrationId($entityType, $fieldName); + return $this->pluginManager->getMigration($id); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/ResolverInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/ResolverInterface.php new file mode 100644 index 0000000000..4d3932003e --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/ResolverInterface.php @@ -0,0 +1,28 @@ +entityType = $entityType; + $this->fieldName = $fieldName; + $this->migrationId = $migrationId; + $this->status = $status ?? static::DEFAULT_STATUS; + } + + /** + * {@inheritDoc} + */ + public function toJson(): string { + return json_encode($this); + } + + /** + * Validate a data object for deserialization. + * + * @param array $data + * The data object. + * + * @throws \Drupal\va_gov_live_field_migration\Exception\StatusDeserializationException + * If the data object is invalid. + */ + protected static function validateData(array $data) { + if (empty($data['entityType'])) { + throw new StatusDeserializationException('Missing entity type.'); + } + if (empty($data['fieldName'])) { + throw new StatusDeserializationException('Missing field name.'); + } + if (empty($data['migrationId'])) { + throw new StatusDeserializationException('Missing migration ID.'); + } + } + + /** + * Deserializes the object from a JSON string. + * + * @param string $json + * The JSON string. + * + * @return \Drupal\va_gov_live_field_migration\Migration\Status\StatusInterface + * The status object. + * + * @throws \Drupal\va_gov_live_field_migration\Exception\StatusDeserializationException + * If the JSON string cannot be deserialized. + */ + public static function fromJson($json): StatusInterface { + $data = json_decode($json, JSON_OBJECT_AS_ARRAY); + if (empty($data)) { + throw new StatusDeserializationException('Unable to deserialize status.'); + } + self::validateData($data); + $result = new Status( + $data['entityType'], + $data['fieldName'], + $data['migrationId'], + $data['status'] ?? static::DEFAULT_STATUS + ); + return $result; + } + + /** + * {@inheritDoc} + */ + public function getKey(): string { + return Key::getKey($this->migrationId, $this->entityType, $this->fieldName); + } + + /** + * {@inheritDoc} + */ + public function getEntityType(): string { + return $this->entityType; + } + + /** + * {@inheritDoc} + */ + public function getFieldName(): string { + return $this->fieldName; + } + + /** + * {@inheritDoc} + */ + public function getMigrationId(): string { + return $this->migrationId; + } + + /** + * {@inheritDoc} + */ + public function getStatus(): string { + return $this->status; + } + + /** + * {@inheritDoc} + */ + public function setStatus(string $status): void { + $this->status = $status; + } + + /** + * {@inheritDoc} + */ + public function jsonSerialize() { + return [ + 'entityType' => $this->getEntityType(), + 'fieldName' => $this->getFieldName(), + 'migrationId' => $this->getMigrationId(), + 'status' => $this->getStatus(), + ]; + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/StatusInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/StatusInterface.php new file mode 100644 index 0000000000..c4290aa2e6 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/StatusInterface.php @@ -0,0 +1,84 @@ +reporter->reportInfo("Successfully completed a migration for $entityType $fieldName."); + } + + /** + * {@inheritDoc} + */ + public function rollbackMigration(string $entityType, string $fieldName) : void { + $this->reporter->reportInfo("Successfully rolled back a migration for $entityType $fieldName."); + } + + /** + * {@inheritDoc} + */ + public function verifyMigration(string $entityType, string $fieldName) : void { + $this->reporter->reportInfo("Successfully verified back a migration for $entityType $fieldName."); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php new file mode 100644 index 0000000000..9f6b825e65 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php @@ -0,0 +1,41 @@ +messenger = $messenger; + $this->logger = $loggerFactory->get('va_gov_live_field_migration'); + } + + /** + * {@inheritDoc} + */ + public function reportInfo(string $message) : void { + $this->messenger->addStatus($message); + $this->logger->info($message); + } + + /** + * {@inheritDoc} + */ + public function reportError(string $message, \Throwable $exception = NULL) : void { + $this->messenger->addError($message); + $this->logger->error($message); + if ($exception) { + // When we are on Drupal 10.0.0, we can use the following: + // Error::logException($this->logger, $exception); + // Until then, we have to do this: + watchdog_exception('va_gov_live_field_migration', $exception); + } + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Reporter/ReporterInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Reporter/ReporterInterface.php new file mode 100644 index 0000000000..4414c753de --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Reporter/ReporterInterface.php @@ -0,0 +1,34 @@ +coreState = $coreState; + } + + /** + * {@inheritDoc} + */ + public function getStatus(string $migrationId, string $entityType, string $fieldName): StatusInterface { + $key = Key::getKey($migrationId, $entityType, $fieldName); + $status = $this->coreState->get($key); + if ($status === NULL) { + throw new StatusNotFoundException("Status not found for key: $key"); + } + return Status::fromJson($status); + } + + /** + * {@inheritDoc} + */ + public function setStatus(StatusInterface $status): void { + $this->coreState->set($status->getKey(), $status->toJson()); + } + + /** + * {@inheritDoc} + */ + public function deleteStatus(string $migrationId, string $entityType, string $fieldName): void { + $key = Key::getKey($migrationId, $entityType, $fieldName); + $this->coreState->delete($key); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php new file mode 100644 index 0000000000..f363e26b9f --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php @@ -0,0 +1,50 @@ +assertInstanceOf(FieldProviderPluginManager::class, $pluginManager); + $this->assertInstanceOf(FieldProviderPluginManagerInterface::class, $pluginManager); + } + +} diff --git a/tests/phpunit/va_gov_live_field_migration/functional/FieldProvider/Resolver/ResolverTest.php b/tests/phpunit/va_gov_live_field_migration/functional/FieldProvider/Resolver/ResolverTest.php new file mode 100644 index 0000000000..dad6b61cee --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/functional/FieldProvider/Resolver/ResolverTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(Resolver::class, $resolver); + $this->assertInstanceOf(ResolverInterface::class, $resolver); + } + +} diff --git a/tests/phpunit/va_gov_live_field_migration/functional/Migration/Plugin/MigrationPluginManagerTest.php b/tests/phpunit/va_gov_live_field_migration/functional/Migration/Plugin/MigrationPluginManagerTest.php new file mode 100644 index 0000000000..7da9bd225a --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/functional/Migration/Plugin/MigrationPluginManagerTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(MigrationPluginManager::class, $pluginManager); + $this->assertInstanceOf(MigrationPluginManagerInterface::class, $pluginManager); + } + +} diff --git a/tests/phpunit/va_gov_live_field_migration/functional/Migration/Resolver/ResolverTest.php b/tests/phpunit/va_gov_live_field_migration/functional/Migration/Resolver/ResolverTest.php new file mode 100644 index 0000000000..0c9d179b78 --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/functional/Migration/Resolver/ResolverTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(Resolver::class, $resolver); + $this->assertInstanceOf(ResolverInterface::class, $resolver); + } + +} diff --git a/tests/phpunit/va_gov_live_field_migration/functional/Reporter/ReporterTest.php b/tests/phpunit/va_gov_live_field_migration/functional/Reporter/ReporterTest.php new file mode 100644 index 0000000000..68b9d210ac --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/functional/Reporter/ReporterTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(Reporter::class, $reporter); + $this->assertInstanceOf(ReporterInterface::class, $reporter); + } + +} diff --git a/tests/phpunit/va_gov_live_field_migration/functional/State/StateTest.php b/tests/phpunit/va_gov_live_field_migration/functional/State/StateTest.php new file mode 100644 index 0000000000..ddc18e9d7f --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/functional/State/StateTest.php @@ -0,0 +1,63 @@ +assertInstanceOf(State::class, $state); + $this->assertInstanceOf(StateInterface::class, $state); + } + + /** + * Test that the service throws an exception when the status is not found. + */ + public function testStatusNotFoundException() { + $this->expectException(StatusNotFoundException::class); + $state = \Drupal::service('va_gov_live_field_migration.state'); + $state->deleteStatus('test_migration', 'node', 'field_test'); + $state->getStatus('test_migration', 'node', 'field_test'); + } + + /** + * Test that the service can set and get status. + * + * @covers ::setStatus + * @covers ::getStatus + */ + public function testSetAndGetStatus() { + $state = \Drupal::service('va_gov_live_field_migration.state'); + $status = new Status('node', 'field_test', 'test_migration', 'not_started'); + $this->assertEquals('test_migration', $status->getMigrationId()); + $this->assertEquals('node', $status->getEntityType()); + $this->assertEquals('field_test', $status->getFieldName()); + $this->assertEquals(StatusInterface::DEFAULT_STATUS, $status->getStatus()); + $this->assertEquals('{"entityType":"node","fieldName":"field_test","migrationId":"test_migration","status":"not_started"}', $status->toJson()); + $status->setStatus('started'); + $state->setStatus($status); + $status = $state->getStatus('test_migration', 'node', 'field_test'); + $this->assertEquals('started', $status->getStatus()); + } + +} diff --git a/tests/phpunit/va_gov_live_field_migration/unit/Migration/Status/StatusTest.php b/tests/phpunit/va_gov_live_field_migration/unit/Migration/Status/StatusTest.php new file mode 100644 index 0000000000..6ed85baab0 --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/unit/Migration/Status/StatusTest.php @@ -0,0 +1,89 @@ +assertInstanceOf(Status::class, $status); + $this->assertEquals('node', $status->getEntityType()); + $this->assertEquals('field_test', $status->getFieldName()); + $this->assertEquals('test_migration', $status->getMigrationId()); + $this->assertEquals('test', $status->getStatus()); + } + + /** + * Test that we can construct a status from arguments, including a default. + */ + public function testConstructWithDefault() { + $status = new Status('node', 'field_test', 'test_migration'); + $this->assertInstanceOf(Status::class, $status); + $this->assertEquals('node', $status->getEntityType()); + $this->assertEquals('field_test', $status->getFieldName()); + $this->assertEquals('test_migration', $status->getMigrationId()); + $this->assertEquals(StatusInterface::DEFAULT_STATUS, $status->getStatus()); + } + + /** + * Test that we can set and get the status. + * + * @covers ::setStatus + * @covers ::getStatus + */ + public function testSetAndGetStatus() { + $status = new Status('node', 'field_test', 'test_migration'); + $this->assertEquals(StatusInterface::DEFAULT_STATUS, $status->getStatus()); + $status->setStatus('test'); + $this->assertEquals('test', $status->getStatus()); + } + + /** + * Test that we can get the key. + * + * @covers ::getKey + */ + public function testGetKey() { + $status = new Status('node', 'field_test', 'test_migration'); + $this->assertEquals('va_gov_live_field_migration__test_migration__node__field_test', $status->getKey()); + } + + /** + * Test that we can serialize and deserialize the status. + * + * @covers ::jsonSerialize + * @covers ::fromJson + * @covers ::toJson + */ + public function testSerialize() { + $status = new Status('node', 'field_test', 'test_migration'); + $this->assertEquals('{"entityType":"node","fieldName":"field_test","migrationId":"test_migration","status":"not_started"}', $status->toJson()); + $status = Status::fromJson('{"entityType":"node","fieldName":"field_test","migrationId":"test_migration","status":"not_started"}'); + $this->assertEquals('node', $status->getEntityType()); + $this->assertEquals('field_test', $status->getFieldName()); + $this->assertEquals('test_migration', $status->getMigrationId()); + $this->assertEquals(StatusInterface::DEFAULT_STATUS, $status->getStatus()); + } + +} From 373b5630649fba609d29c55353334152f7cdfe2a Mon Sep 17 00:00:00 2001 From: Nathan Douglas Date: Tue, 12 Sep 2023 08:51:02 -0400 Subject: [PATCH 2/5] Adds unit test for reporter service. --- .../unit/Reporter/ReporterTest.php | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/phpunit/va_gov_live_field_migration/unit/Reporter/ReporterTest.php diff --git a/tests/phpunit/va_gov_live_field_migration/unit/Reporter/ReporterTest.php b/tests/phpunit/va_gov_live_field_migration/unit/Reporter/ReporterTest.php new file mode 100644 index 0000000000..2fd5b33222 --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/unit/Reporter/ReporterTest.php @@ -0,0 +1,90 @@ +prophesize(MessengerInterface::class); + $messenger = $messengerProphecy->reveal(); + $loggerProphecy = $this->prophesize(LoggerInterface::class); + $logger = $loggerProphecy->reveal(); + $loggerChannelFactoryProphecy = $this->prophesize(LoggerChannelFactoryInterface::class); + $loggerChannelFactoryProphecy->get('va_gov_live_field_migration')->willReturn($logger); + $loggerChannelFactory = $loggerChannelFactoryProphecy->reveal(); + $reporter = new Reporter($messenger, $loggerChannelFactory); + $this->assertInstanceOf(Reporter::class, $reporter); + } + + /** + * Test that the reporter object will handle an error correctly. + * + * @covers ::reportError + */ + public function testReportError() { + $messengerProphecy = $this->prophesize(MessengerInterface::class); + $messengerProphecy->addError('message')->shouldBeCalled(); + $messenger = $messengerProphecy->reveal(); + $loggerProphecy = $this->prophesize(LoggerInterface::class); + $loggerProphecy->error('message', Argument::cetera())->shouldBeCalled(); + $loggerProphecy->log(Argument::cetera())->shouldBeCalled(); + $logger = $loggerProphecy->reveal(); + $stringTranslationProphecy = $this->prophesize(TranslationInterface::class); + $stringTranslationProphecy->translateString(Argument::any())->will(function ($args) { + return $args[0]->getUntranslatedString(); + }); + $stringTranslationService = $stringTranslationProphecy->reveal(); + $containerProphecy = $this->prophesize(ContainerInterface::class); + $containerProphecy->get('string_translation')->willReturn($stringTranslationService); + $loggerChannelFactoryProphecy = $this->prophesize(LoggerChannelFactoryInterface::class); + $loggerChannelFactoryProphecy->get('va_gov_live_field_migration')->willReturn($logger); + $loggerChannelFactory = $loggerChannelFactoryProphecy->reveal(); + $containerProphecy->get('logger.factory')->willReturn($loggerChannelFactory); + $container = $containerProphecy->reveal(); + \Drupal::setContainer($container); + $reporter = new Reporter($messenger, $loggerChannelFactory); + $reporter->reportError('message', new \Exception('test')); + } + + /** + * Test that the reporter object will handle an info message correctly. + * + * @covers ::reportInfo + */ + public function testReportInfo() { + $messengerProphecy = $this->prophesize(MessengerInterface::class); + $messengerProphecy->addStatus('message')->shouldBeCalled(); + $messenger = $messengerProphecy->reveal(); + $loggerProphecy = $this->prophesize(LoggerInterface::class); + $loggerProphecy->info('message', Argument::cetera())->shouldBeCalled(); + $logger = $loggerProphecy->reveal(); + $loggerChannelFactoryProphecy = $this->prophesize(LoggerChannelFactoryInterface::class); + $loggerChannelFactoryProphecy->get('va_gov_live_field_migration')->willReturn($logger); + $loggerChannelFactory = $loggerChannelFactoryProphecy->reveal(); + $reporter = new Reporter($messenger, $loggerChannelFactory); + $reporter->reportInfo('message'); + } + +} From 734809b053dc5ae1fcfba642de87c42a8cb066fd Mon Sep 17 00:00:00 2001 From: Nathan Douglas Date: Thu, 14 Sep 2023 08:45:07 -0400 Subject: [PATCH 3/5] Improves reporting in a Drush context. --- .../drush.services.yml | 2 +- .../src/Commands/Commands.php | 95 ++++-------- .../src/Migration/Runner/Runner.php | 138 ++++++++++++++++++ .../src/Migration/Runner/RunnerInterface.php | 94 ++++++++++++ .../src/Reporter/Reporter.php | 25 +++- .../va_gov_live_field_migration.services.yml | 7 + .../Migration/Runner/RunnerTest.php | 30 ++++ 7 files changed, 321 insertions(+), 70 deletions(-) create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/RunnerInterface.php create mode 100644 tests/phpunit/va_gov_live_field_migration/functional/Migration/Runner/RunnerTest.php diff --git a/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml b/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml index 5815781a69..e3d75523e6 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml +++ b/docroot/modules/custom/va_gov_live_field_migration/drush.services.yml @@ -3,6 +3,6 @@ services: class: \Drupal\va_gov_live_field_migration\Commands\Commands arguments: - '@va_gov_live_field_migration.field_provider_resolver' - - '@va_gov_live_field_migration.migration_resolver' + - '@va_gov_live_field_migration.migration_runner' tags: - { name: drush.command } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php b/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php index b298d92948..01ad40d017 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php @@ -3,7 +3,7 @@ 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\Resolver\ResolverInterface as MigrationResolverInterface; +use Drupal\va_gov_live_field_migration\Migration\Runner\RunnerInterface; use Drush\Commands\DrushCommands; /** @@ -19,48 +19,26 @@ class Commands extends DrushCommands { protected $fieldProviderResolver; /** - * The migration resolver service. + * The migration runner service. * - * @var \Drupal\va_gov_live_field_migration\Migration\Resolver\ResolverInterface + * @var \Drupal\va_gov_live_field_migration\Migration\Runner\RunnerInterface */ - protected $migrationResolver; + 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\Resolver\ResolverInterface $migrationResolver - * The migration resolver service. + * @param \Drupal\va_gov_live_field_migration\Migration\Runner\RunnerInterface $migrationRunner + * The migration runner service. */ public function __construct( FieldProviderResolverInterface $fieldProviderResolver, - MigrationResolverInterface $migrationResolver + RunnerInterface $migrationRunner ) { - $this->migrationResolver = $migrationResolver; $this->fieldProviderResolver = $fieldProviderResolver; - } - - /** - * Perform an operation, such as migrating, rolling back, or verifying. - * - * @param callable $operation - * The operation to perform. - */ - 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'); - } + $this->migrationRunner = $migrationRunner; } /** @@ -71,20 +49,15 @@ public function performOperation(callable $operation) { * @param string $fieldName * The field name. * - * @command va-gov-live-field-migration:migrate-field - * @aliases va-gov-live-field-migration-migrate-field + * @command va-gov-live-field-migration:migrate + * @aliases va-gov-live-field-migration-migrate */ - public function migrateField( + public function migrate( string $entityType, string $fieldName ) { - $this->performOperation(function () use ($entityType, $fieldName) { - $this->output()->writeln('Migrating field "' . $fieldName . '" on entity type "' . $entityType . '"...'); - $this->migrationResolver - ->getMigration($entityType, $fieldName) - ->runMigration($entityType, $fieldName); - $this->output()->writeln('Migration successful.'); - }); + $migration = $this->migrationRunner->getMigration($entityType, $fieldName); + $this->migrationRunner->runMigration($migration, $entityType, $fieldName); } /** @@ -95,20 +68,15 @@ public function migrateField( * @param string $fieldName * The field name. * - * @command va-gov-live-field-migration:rollback-field - * @aliases va-gov-live-field-migration-rollback-field + * @command va-gov-live-field-migration:rollback + * @aliases va-gov-live-field-migration-rollback */ - public function rollbackField( + public function rollback( string $entityType, string $fieldName ) { - $this->performOperation(function () use ($entityType, $fieldName) { - $this->output()->writeln('Rolling back migration of field "' . $fieldName . '" on entity type "' . $entityType . '"...'); - $this->migrationResolver - ->getMigration($entityType, $fieldName) - ->rollbackMigration($entityType, $fieldName); - $this->output()->writeln('Rollback successful.'); - }); + $migration = $this->migrationRunner->getMigration($entityType, $fieldName); + $this->migrationRunner->rollbackMigration($migration, $entityType, $fieldName); } /** @@ -126,13 +94,8 @@ public function verify( string $entityType, string $fieldName ) { - $this->performOperation(function () use ($entityType, $fieldName) { - $this->output()->writeln('Verifying migration of field "' . $fieldName . '" on entity type "' . $entityType . '"...'); - $this->migrationResolver - ->getMigration($entityType, $fieldName) - ->verifyMigration($entityType, $fieldName); - $this->output()->writeln('Verification successful.'); - }); + $migration = $this->migrationRunner->getMigration($entityType, $fieldName); + $this->migrationRunner->verifyMigration($migration, $entityType, $fieldName); } /** @@ -159,16 +122,14 @@ public function find( if ($entityType === NULL) { $entityType = 'node'; } - $this->performOperation(function () use ($fieldProvider, $entityType, $bundle) { - $this->output()->writeln('Finding fields 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); - } - }); + $this->output()->writeln('Finding fields 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); + } } } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php new file mode 100644 index 0000000000..37325a2c4a --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php @@ -0,0 +1,138 @@ +fieldProviderResolver = $fieldProviderResolver; + $this->migrationResolver = $migrationResolver; + $this->reporter = $reporter; + $this->state = $state; + } + + /** + * Perform an operation, such as migrating, rolling back, or verifying. + * + * @param callable $operation + * The operation to perform. + */ + public function performOperation(callable $operation) { + $startTime = microtime(TRUE); + try { + $operation(); + } + catch (\Exception $exception) { + $this->reporter->reportError('Error: ' . $exception->getMessage()); + } + finally { + $elapsedTime = microtime(TRUE) - $startTime; + $peakMemoryUsage = memory_get_peak_usage(); + $this->reporter->reportInfo('Elapsed time: ' . number_format($elapsedTime, 2) . ' seconds'); + $this->reporter->reportInfo('Peak memory usage: ' . number_format($peakMemoryUsage / 1024 / 1024, 2) . ' MB'); + } + } + + /** + * {@inheritdoc} + */ + public function getFieldProvider(string $id): FieldProviderPluginInterface { + return $this->fieldProviderResolver->getFieldProvider($id); + } + + /** + * {@inheritdoc} + */ + public function getMigration(string $entityType, string $fieldName) : MigrationPluginInterface { + return $this->migrationResolver->getMigration($entityType, $fieldName); + } + + /** + * {@inheritdoc} + */ + public function runMigration(MigrationPluginInterface $migration, string $entityType, string $fieldName) : void { + $this->performOperation(function () use ($migration, $entityType, $fieldName) { + $this->reporter->reportInfo('Migrating field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $migration->runMigration($entityType, $fieldName); + $this->reporter->reportInfo('Migration successful.'); + }); + } + + /** + * {@inheritdoc} + */ + public function rollbackMigration(MigrationPluginInterface $migration, string $entityType, string $fieldName) : void { + $this->performOperation(function () use ($migration, $entityType, $fieldName) { + $this->reporter->reportInfo('Rolling back field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $migration->rollbackMigration($entityType, $fieldName); + $this->reporter->reportInfo('Rollback successful.'); + }); + } + + /** + * {@inheritdoc} + */ + public function verifyMigration(MigrationPluginInterface $migration, string $entityType, string $fieldName) : void { + $this->performOperation(function () use ($migration, $entityType, $fieldName) { + $this->reporter->reportInfo('Verifying field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $migration->rollbackMigration($entityType, $fieldName); + $this->reporter->reportInfo('Verification successful.'); + }); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/RunnerInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/RunnerInterface.php new file mode 100644 index 0000000000..33ebb3cafb --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/RunnerInterface.php @@ -0,0 +1,94 @@ +logger = $loggerFactory->get('va_gov_live_field_migration'); } + /** + * Check if we're in a Drush environment. + * + * @return bool + * TRUE if in a Drush environment, FALSE otherwise. + */ + protected function isDrushEnvironment() : bool { + return class_exists(Drush::class); + } + /** * {@inheritDoc} */ public function reportInfo(string $message) : void { - $this->messenger->addStatus($message); + if ($this->isDrushEnvironment()) { + Drush::service('output')->writeln($message); + } + else { + $this->messenger->addStatus($message); + } $this->logger->info($message); } @@ -53,7 +69,12 @@ public function reportInfo(string $message) : void { * {@inheritDoc} */ public function reportError(string $message, \Throwable $exception = NULL) : void { - $this->messenger->addError($message); + if ($this->isDrushEnvironment()) { + Drush::service('output')->writeln("$message"); + } + else { + $this->messenger->addError($message); + } $this->logger->error($message); if ($exception) { // When we are on Drupal 10.0.0, we can use the following: diff --git a/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml b/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml index ad122b9ab4..33d84d11a5 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml +++ b/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml @@ -14,6 +14,13 @@ services: arguments: - '@plugin.manager.va_gov_live_field_migration.migration' - '@entity_field.manager' + va_gov_live_field_migration.migration_runner: + class: Drupal\va_gov_live_field_migration\Migration\Runner\Runner + arguments: + - '@va_gov_live_field_migration.field_provider_resolver' + - '@va_gov_live_field_migration.migration_resolver' + - '@va_gov_live_field_migration.reporter' + - '@va_gov_live_field_migration.state' va_gov_live_field_migration.reporter: class: Drupal\va_gov_live_field_migration\Reporter\Reporter arguments: ['@messenger', '@logger.factory'] diff --git a/tests/phpunit/va_gov_live_field_migration/functional/Migration/Runner/RunnerTest.php b/tests/phpunit/va_gov_live_field_migration/functional/Migration/Runner/RunnerTest.php new file mode 100644 index 0000000000..6016b6b33a --- /dev/null +++ b/tests/phpunit/va_gov_live_field_migration/functional/Migration/Runner/RunnerTest.php @@ -0,0 +1,30 @@ +assertInstanceOf(Runner::class, $runner); + $this->assertInstanceOf(RunnerInterface::class, $runner); + } + +} From 93adcea57c459eec5a1866b0450902e0614643c5 Mon Sep 17 00:00:00 2001 From: Nathan Douglas Date: Thu, 14 Sep 2023 09:37:57 -0400 Subject: [PATCH 4/5] Adds #14995-specific logic. --- .../src/Commands/Commands.php | 56 +++---- .../src/Migration/Runner/Runner.php | 6 +- .../src/Plugin/FieldProvider/Issue14995.php | 145 ++++++++++++++++++ 3 files changed, 173 insertions(+), 34 deletions(-) create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php b/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php index 01ad40d017..d816ca64c4 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Commands/Commands.php @@ -4,6 +4,7 @@ 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; /** @@ -48,10 +49,10 @@ public function __construct( * The entity type. * @param string $fieldName * The field name. - * - * @command va-gov-live-field-migration:migrate - * @aliases va-gov-live-field-migration-migrate */ + #[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 $fieldName @@ -67,10 +68,10 @@ public function migrate( * The entity type. * @param string $fieldName * The field name. - * - * @command va-gov-live-field-migration:rollback - * @aliases va-gov-live-field-migration-rollback */ + #[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 $fieldName @@ -86,10 +87,10 @@ public function rollback( * The entity 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 $fieldName @@ -100,29 +101,22 @@ public function verify( /** * Find fields that haven't been migrated yet. - * - * @param string|null $fieldProvider - * The field provider. - * @param string|null $entityType - * The entity type. - * @param string|null $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 $fieldProvider = NULL, - string $entityType = NULL, - string $bundle = NULL - ) { - if ($fieldProvider === NULL) { - $fieldProvider = 'test_empty_list'; - } - if ($entityType === NULL) { - $entityType = 'node'; - } - $this->output()->writeln('Finding fields on entity type "' . $entityType . '", bundle "' . ($bundle ?: 'NULL') . '"...'); + #[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 = [ + // 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, + ]) { + $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); diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php index 37325a2c4a..5001fb1a82 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Runner/Runner.php @@ -107,7 +107,7 @@ public function getMigration(string $entityType, string $fieldName) : MigrationP */ public function runMigration(MigrationPluginInterface $migration, string $entityType, string $fieldName) : void { $this->performOperation(function () use ($migration, $entityType, $fieldName) { - $this->reporter->reportInfo('Migrating field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $this->reporter->reportInfo('Migrating field "' . $fieldName . '" on entity type "' . $entityType . '" with migration "' . $migration->getPluginId() . '"...'); $migration->runMigration($entityType, $fieldName); $this->reporter->reportInfo('Migration successful.'); }); @@ -118,7 +118,7 @@ public function runMigration(MigrationPluginInterface $migration, string $entity */ public function rollbackMigration(MigrationPluginInterface $migration, string $entityType, string $fieldName) : void { $this->performOperation(function () use ($migration, $entityType, $fieldName) { - $this->reporter->reportInfo('Rolling back field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $this->reporter->reportInfo('Rolling back field "' . $fieldName . '" on entity type "' . $entityType . '" with migration "' . $migration->getPluginId() . '"...'); $migration->rollbackMigration($entityType, $fieldName); $this->reporter->reportInfo('Rollback successful.'); }); @@ -129,7 +129,7 @@ public function rollbackMigration(MigrationPluginInterface $migration, string $e */ public function verifyMigration(MigrationPluginInterface $migration, string $entityType, string $fieldName) : void { $this->performOperation(function () use ($migration, $entityType, $fieldName) { - $this->reporter->reportInfo('Verifying field "' . $fieldName . '" on entity type "' . $entityType . '"...'); + $this->reporter->reportInfo('Verifying field "' . $fieldName . '" on entity type "' . $entityType . '" with migration "' . $migration->getPluginId() . '"...'); $migration->rollbackMigration($entityType, $fieldName); $this->reporter->reportInfo('Verification successful.'); }); diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php new file mode 100644 index 0000000000..825c98debb --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php @@ -0,0 +1,145 @@ + [ + 'field_phone_label', + 'field_alert_heading', + 'field_text_expander', + 'field_error_message', + 'field_section_header', + 'field_question', + 'field_email_label', + 'field_button_label', + 'field_loading_message', + 'field_short_phrase_with_a_number', + 'field_title', + 'field_link_summary', + ], + 'node' => [ + 'field_teaser_text', + 'field_description', + 'field_home_page_hub_label', + ], + ]; + + const VALID_FIELD_TYPES = [ + 'string', + 'text', + ]; + + /** + * The entity field manager. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface + */ + protected EntityFieldManagerInterface $entityFieldManager; + + /** + * {@inheritDoc} + * + * @codeCoverageIgnore + */ + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + TranslationInterface $stringTranslation, + EntityFieldManagerInterface $entityFieldManager + ) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $stringTranslation); + $this->entityFieldManager = $entityFieldManager; + } + + /** + * {@inheritDoc} + * + * @codeCoverageIgnore + */ + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition + ) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('string_translation'), + $container->get('entity_field.manager') + ); + } + + /** + * Determine whether this field is still a valid target. + * + * @param string $entityType + * The entity type. + * @param string $fieldName + * The field name. + * + * @return bool + * Whether this field is still a valid target. + */ + public function isStillValid(string $entityType, string $fieldName) : bool { + $fieldStorage = $this->entityFieldManager->getFieldStorageDefinitions($entityType)[$fieldName]; + return in_array($fieldStorage->getType(), static::VALID_FIELD_TYPES); + } + + /** + * Determine whether this field exists on a specified bundle. + * + * @param string $entityType + * The entity type. + * @param string $fieldName + * The field name. + * @param string $bundle + * The bundle. + * + * @return bool + * Whether this field exists on a specified bundle. + */ + public function fieldExistsOnBundle(string $entityType, string $fieldName, string $bundle) : bool { + $fieldMap = $this->entityFieldManager->getFieldMapByFieldType($entityType, $fieldName); + return in_array($bundle, array_keys($fieldMap)); + } + + /** + * {@inheritDoc} + */ + public function getFields(string $entityType, string $bundle = NULL) : array { + $fields = static::FIELDS[$entityType] ?? []; + if ($bundle !== NULL) { + $fields = array_filter($fields, function ($field) use ($bundle) { + return $this->fieldExistsOnBundle($entityType, $field, $bundle); + }); + } + $fields = array_filter($fields, function ($field) use ($entityType) { + return $this->isStillValid($entityType, $field); + }); + return $fields; + } + +} From 53d7a77a948f0b863ef0d8e74ad8f0c6cbb0f365 Mon Sep 17 00:00:00 2001 From: Nathan Douglas Date: Wed, 20 Sep 2023 13:49:22 -0400 Subject: [PATCH 5/5] WIP. --- .../src/Database/Database.php | 116 ++++ .../src/Database/DatabaseInterface.php | 127 ++++ .../src/Exception/MigrationErrorException.php | 2 +- .../MigrationFieldNotFoundException.php | 8 + .../MigrationFieldWrongTypeException.php | 8 + .../src/Exception/MigrationRunException.php | 8 + .../src/FieldPurger/FieldPurger.php | 17 + .../src/FieldPurger/FieldPurgerInterface.php | 18 + .../Migration/Plugin/MigrationPluginBase.php | 17 +- .../src/Migration/Resolver/Resolver.php | 3 + .../src/Migration/Status/Status.php | 3 +- .../src/Migrator/Factory/Factory.php | 196 ++++++ .../src/Migrator/Factory/FactoryInterface.php | 38 ++ .../src/Migrator/MigratorBase.php | 582 ++++++++++++++++++ .../src/Migrator/MigratorInterface.php | 34 + .../Migrator/StringToStringLongMigrator.php | 10 + .../src/Migrator/TextToStringLongMigrator.php | 10 + .../Traits/DatabaseMigrationInterface.php | 27 + .../Traits/DatabaseMigrationTrait.php | 64 ++ .../EntityDisplayOperationsInterface.php | 61 ++ .../Traits/EntityDisplayOperationsTrait.php | 70 +++ .../FieldStorageOperationsInterface.php | 68 ++ .../Traits/FieldStorageOperationsTrait.php | 143 +++++ .../Traits/MigrationStatusInterface.php | 54 ++ .../Migrator/Traits/MigrationStatusTrait.php | 89 +++ .../src/Plugin/FieldProvider/Issue14995.php | 9 +- .../Plugin/Migration/StringToStringLong.php | 12 +- .../src/Plugin/Migration/TestException.php | 4 +- .../src/Plugin/Migration/TextToStringLong.php | 12 +- .../src/State/State.php | 9 + .../src/State/StateInterface.php | 15 + .../va_gov_live_field_migration.services.yml | 19 + 32 files changed, 1831 insertions(+), 22 deletions(-) create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Database/Database.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Database/DatabaseInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationFieldNotFoundException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationFieldWrongTypeException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Exception/MigrationRunException.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldPurger/FieldPurger.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/FieldPurger/FieldPurgerInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/Factory.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/FactoryInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/MigratorBase.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/MigratorInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/StringToStringLongMigrator.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/TextToStringLongMigrator.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/DatabaseMigrationInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/DatabaseMigrationTrait.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/EntityDisplayOperationsInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/EntityDisplayOperationsTrait.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/FieldStorageOperationsInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/FieldStorageOperationsTrait.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/MigrationStatusInterface.php create mode 100644 docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/MigrationStatusTrait.php diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Database/Database.php b/docroot/modules/custom/va_gov_live_field_migration/src/Database/Database.php new file mode 100644 index 0000000000..2830386ecf --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Database/Database.php @@ -0,0 +1,116 @@ +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); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Database/DatabaseInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Database/DatabaseInterface.php new file mode 100644 index 0000000000..b13c0a76be --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Database/DatabaseInterface.php @@ -0,0 +1,127 @@ +reporter = $reporter; $this->stringTranslation = $stringTranslation; + $this->reporter = $reporter; + $this->migratorFactory = $migratorFactory; } /** @@ -55,8 +65,9 @@ public static function create( $configuration, $plugin_id, $plugin_definition, + $container->get('string_translation'), $container->get('va_gov_live_field_migration.reporter'), - $container->get('string_translation') + $container->get('va_gov_live_field_migration.migrator_factory') ); } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/Resolver.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/Resolver.php index 5a8086d144..9f314061a5 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/Resolver.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Resolver/Resolver.php @@ -62,6 +62,9 @@ public function __construct( */ public function getMigrationId(string $entityType, string $fieldName): string { $fieldStorage = $this->entityFieldManager->getFieldStorageDefinitions($entityType)[$fieldName]; + if ($fieldStorage === NULL) { + throw new MigrationNotFoundException(sprintf('Field %s not found on entity type %s.', $fieldName, $entityType)); + } if ($fieldStorage->getType() === 'string') { return 'string_to_string_long'; } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Status.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Status.php index c28dcaf8dd..e86aaa0679 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Status.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migration/Status/Status.php @@ -2,6 +2,7 @@ namespace Drupal\va_gov_live_field_migration\Migration\Status; +use Drupal\va_gov_live_field_migration\Exception\StatusDeserializationException; use Drupal\va_gov_live_field_migration\Migration\Status\Key\Key; /** @@ -161,7 +162,7 @@ public function setStatus(string $status): void { /** * {@inheritDoc} */ - public function jsonSerialize() { + public function jsonSerialize(): mixed { return [ 'entityType' => $this->getEntityType(), 'fieldName' => $this->getFieldName(), diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/Factory.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/Factory.php new file mode 100644 index 0000000000..8773a5c2b1 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/Factory.php @@ -0,0 +1,196 @@ +stringTranslation = $stringTranslation; + $this->reporter = $reporter; + $this->state = $state; + $this->database = $database; + $this->entityDisplayRepository = $entityDisplayRepository; + $this->entityFieldManager = $entityFieldManager; + $this->entityTypeManager = $entityTypeManager; + $this->entityTypeRepository = $entityTypeRepository; + $this->fieldPurger = $fieldPurger; + } + + /** + * {@inheritDoc} + */ + protected function getReporter(): ReporterInterface { + return $this->reporter; + } + + /** + * {@inheritDoc} + */ + protected function getState(): StateInterface { + return $this->state; + } + + /** + * {@inheritDoc} + */ + protected function getDatabase(): DatabaseInterface { + return $this->database; + } + + /** + * {@inheritDoc} + */ + protected function getEntityDisplayRepository(): EntityDisplayRepositoryInterface { + return $this->entityDisplayRepository; + } + + /** + * {@inheritDoc} + */ + protected function getEntityFieldManager(): EntityFieldManagerInterface { + return $this->entityFieldManager; + } + + /** + * {@inheritDoc} + */ + protected function getEntityTypeManager(): EntityTypeManagerInterface { + return $this->entityTypeManager; + } + + /** + * {@inheritDoc} + */ + protected function getEntityTypeRepository(): EntityTypeRepositoryInterface { + return $this->entityTypeRepository; + } + + /** + * {@inheritDoc} + */ + public function getStringToStringLongMigrator(string $entityType, string $fieldName): MigratorInterface { + return new StringToStringLongMigrator( + $this->reporter, + $this->state, + $this->database, + $this->stringTranslation, + $this->entityDisplayRepository, + $this->entityFieldManager, + $this->entityTypeManager, + $this->entityTypeRepository, + $this->fieldPurger, + $entityType, + $fieldName + ); + } + + /** + * {@inheritDoc} + */ + public function getTextToStringLongMigrator(string $entityType, string $fieldName): MigratorInterface { + return new TextToStringLongMigrator( + $this->reporter, + $this->state, + $this->database, + $this->stringTranslation, + $this->entityDisplayRepository, + $this->entityFieldManager, + $this->entityTypeManager, + $this->entityTypeRepository, + $this->fieldPurger, + $entityType, + $fieldName + ); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/FactoryInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/FactoryInterface.php new file mode 100644 index 0000000000..435f8b83fa --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Factory/FactoryInterface.php @@ -0,0 +1,38 @@ +reporter = $reporter; + $this->state = $state; + $this->database = $database; + $this->stringTranslation = $stringTranslation; + $this->entityDisplayRepository = $entityDisplayRepository; + $this->entityFieldManager = $entityFieldManager; + $this->entityTypeManager = $entityTypeManager; + $this->entityTypeRepository = $entityTypeRepository; + $this->fieldPurger = $fieldPurger; + $this->entityType = $entityType; + $this->fieldName = $fieldName; + } + + /** + * {@inheritDoc} + */ + public function getEntityType(): string { + return $this->entityType; + } + + /** + * {@inheritDoc} + */ + public function getFieldName(): string { + return $this->fieldName; + } + + /** + * {@inheritDoc} + */ + protected function getState(): StateInterface { + return $this->state; + } + + /** + * {@inheritDoc} + */ + protected function getReporter(): ReporterInterface { + return $this->reporter; + } + + /** + * {@inheritDoc} + */ + protected function getDatabase(): DatabaseInterface { + return $this->database; + } + + /** + * {@inheritDoc} + */ + protected function getEntityDisplayRepository(): EntityDisplayRepositoryInterface { + return $this->entityDisplayRepository; + } + + /** + * {@inheritDoc} + */ + protected function getEntityFieldManager(): EntityFieldManagerInterface { + return $this->entityFieldManager; + } + + /** + * {@inheritDoc} + */ + protected function getEntityTypeManager(): EntityTypeManagerInterface { + return $this->entityTypeManager; + } + + /** + * {@inheritDoc} + */ + protected function getEntityTypeRepository(): EntityTypeRepositoryInterface { + return $this->entityTypeRepository; + } + + /** + * Get the source type for this migration. + * + * @return string + * The source type. + */ + protected function getSourceType(): string { + return 'string'; + } + + /** + * Get the destination type for this migration. + * + * @return string + * The destination type. + */ + protected function getDestinationType(): string { + return 'string_long'; + } + + /** + * Runs a migration. + * + * @throws \Drupal\va_gov_live_field_migration\Exception\MigrationRunException + * If the migration run fails. + */ + public function run() { + $this->verifyField($this->getSourceType()); + $this->backupFieldTables(); + $this->fieldConfigs = []; + $this->formDisplayConfigs = []; + $this->viewDisplayConfigs = []; + $this->backupFieldStorageConfig(); + $bundles = $this->getFieldBundles(); + foreach ($bundles as $bundle => $label) { + $this->reporter->reportInfo("Backing up data for field {$this->fieldName} on bundle {$bundle} ({$label})..."); + $this->backupFieldConfig($bundle); + $this->backupFormDisplayConfig($bundle); + $this->backupViewDisplayConfig($bundle); + } + $this->deleteFieldStorageConfig(); + $this->fieldPurger->purge(); + $this->alterFieldStorageConfig($this->getDestinationType()); + $this->createFieldStorageConfig($this->workingFieldStorageConfig); + $this->alterFieldConfigs($this->getDestinationType()); + $this->createFieldConfigs($this->workingFieldConfigs); + foreach ($bundles as $bundle => $label) { + $this->alterFormDisplayConfig($bundle); + $this->alterViewDisplayConfig($bundle); + $this->updateFormDisplayConfig($bundle); + $this->updateViewDisplayConfig($bundle); + } + // $this->database + // ->restorePrimaryFieldTable($this->entityType, $this->fieldName, TRUE); + // $this->database + // ->restoreFieldRevisionTable($this->entityType, $this->fieldName, TRUE); + } + + /** + * Backup the field storage config. + */ + public function backupFieldStorageConfig(): void { + $this->reporter->reportInfo("Backing up field storage config for field {$this->fieldName}..."); + $fieldStorageConfig = $this->getFieldStorageConfig(); + $fieldStorageConfigArray = $fieldStorageConfig->toArray(); + $this->fieldStorageConfigBackup = $fieldStorageConfigArray; + $this->workingFieldStorageConfig = $fieldStorageConfigArray; + } + + /** + * Alter the field storage config. + * + * @param string $destinationType + * The destination type. + * @param string $destinationSettings + * The destination settings. + */ + public function alterFieldStorageConfig(string $destinationType, array $destinationSettings = []): void { + $this->reporter->reportInfo("Altering field storage config for field {$this->fieldName}..."); + $this->workingFieldStorageConfig['type'] = $destinationType; + $this->workingFieldStorageConfig['settings'] = $destinationSettings; + } + + /** + * Restore the field storage config. + * + * @param array $fieldStorageConfig + * The field storage config. + */ + public function restoreFieldStorageConfig(array $fieldStorageConfig): void { + $this->reporter->reportInfo("Restoring field storage config for field {$this->fieldName}..."); + $this->workingFieldStorageConfig = $fieldStorageConfig; + } + + /** + * Backup the field config. + * + * @param string $bundle + * The bundle. + */ + public function backupFieldConfig(string $bundle): void { + $this->reporter->reportInfo("Backing up field config for field {$this->fieldName} on bundle {$bundle}..."); + $fieldConfig = $this->getFieldConfig($bundle); + $fieldConfigArray = $fieldConfig->toArray(); + $this->fieldConfigBackups[$bundle] = $fieldConfigArray; + $this->workingFieldConfigs[$bundle] = $fieldConfigArray; + } + + /** + * Alter the field configs. + * + * @param string $destinationType + * The destination type. + * @param string $destinationSettings + * The destination settings. + */ + public function alterFieldConfigs(string $destinationType, array $destinationSettings = []): void { + $this->reporter->reportInfo("Altering field configs for field {$this->fieldName}..."); + foreach ($this->workingFieldConfigs as $bundle => $fieldConfig) { + $this->alterFieldConfig($bundle, $destinationType, $destinationSettings); + } + } + + /** + * Alter the field config. + * + * @param string $bundle + * The bundle. + * @param string $destinationType + * The destination type. + * @param string $destinationSettings + * The destination settings. + */ + public function alterFieldConfig(string $bundle, string $destinationType, array $destinationSettings = []): void { + $this->reporter->reportInfo("Altering field config for field {$this->fieldName} on bundle {$bundle}..."); + $this->workingFieldConfigs[$bundle]['type'] = $destinationType; + $this->workingFieldConfigs[$bundle]['settings'] = $destinationSettings; + } + + /** + * Backup the form display config. + * + * @param string $bundle + * The bundle. + */ + public function backupFormDisplayConfig(string $bundle): void { + $this->reporter->reportInfo("Backing up form display config for field {$this->fieldName} on bundle {$bundle}..."); + $formModeOptions = $this->getFormModeOptions($bundle); + foreach ($formModeOptions as $formMode => $options) { + $formDisplayConfig = $this->getFormDisplayConfig($bundle, $formMode); + $formDisplayConfigArray = $formDisplayConfig->toArray(); + $this->formDisplayConfigBackups[$bundle][$formMode] = $formDisplayConfigArray; + $this->workingFormDisplayConfigs[$bundle][$formMode] = $formDisplayConfigArray; + } + } + + /** + * Alter the form display config. + * + * @param string $bundle + * The bundle. + */ + public function alterFormDisplayConfig(string $bundle): void { + $this->reporter->reportInfo("Altering form display config for field {$this->fieldName} on bundle {$bundle}..."); + $formModeOptions = $this->getFormModeOptions($bundle); + foreach ($formModeOptions as $formMode => $options) { + $this->alterFormDisplayConfigForMode($bundle, $formMode); + } + } + + /** + * Alter the form display config for a specific mode. + * + * @param string $bundle + * The bundle. + * @param string $formMode + * The form mode. + */ + public function alterFormDisplayConfigForMode(string $bundle, string $formMode): void { + $this->reporter->reportInfo("Altering form display config for field {$this->fieldName} on bundle {$bundle} for form mode {$formMode}..."); + $savedConfig = $this->workingFormDisplayConfigs[$bundle][$formMode]['content']; + $fieldName = $this->fieldName; + // If not present, then it is disabled and we don't need to care about it. + if (!isset($savedConfig[$fieldName])) { + return; + } + $desiredType = $this->getDestinationType() . '_textfield_with_counter'; + // This already has the desired configuration. + if ($savedConfig[$fieldName]['type'] === $desiredType) { + return; + } + $savedConfig[$fieldName]['type'] = $desiredType; + $savedConfig[$fieldName]['settings']['maxlength'] = $this->workingFieldStorageConfig['settings']['max_length']; + $this->workingFormDisplayConfigs[$bundle][$formMode]['content'] = $savedConfig; + } + + /** + * Update the form display config. + * + * @param string $bundle + * The bundle. + */ + public function updateFormDisplayConfig(string $bundle): void { + $this->reporter->reportInfo("Updating form display config for field {$this->fieldName} on bundle {$bundle}..."); + $formModeOptions = $this->getFormModeOptions($bundle); + foreach ($formModeOptions as $formMode => $options) { + $this->updateFormDisplayConfigForMode($bundle, $formMode, $this->workingFormDisplayConfigs[$bundle][$formMode]['content']); + } + } + + /** + * Update the form display config for a specific mode. + * + * @param string $bundle + * The bundle. + * @param string $formMode + * The form mode. + * @param array $config + * The config. + */ + public function updateFormDisplayConfigForMode(string $bundle, string $formMode, array $config): void { + $this->reporter->reportInfo("Updating form display config for field {$this->fieldName} on bundle {$bundle} for form mode {$formMode}..."); + $this->getEntityDisplayRepository() + ->getFormDisplay($this->entityType, $bundle, $formMode) + ->setComponent($this->fieldName, $config) + ->save(); + } + + /** + * Backup the view display config. + * + * @param string $bundle + * The bundle. + */ + public function backupViewDisplayConfig(string $bundle): void { + $this->reporter->reportInfo("Backing up view display config for field {$this->fieldName} on bundle {$bundle}..."); + $viewModeOptions = $this->getViewModeOptions($bundle); + foreach ($viewModeOptions as $viewMode => $options) { + $viewDisplayConfig = $this->getViewDisplayConfig($bundle, $viewMode); + $viewDisplayConfigArray = $viewDisplayConfig->toArray(); + $this->viewDisplayConfigBackups[$bundle][$viewMode] = $viewDisplayConfigArray; + $this->workingViewDisplayConfigs[$bundle][$viewMode] = $viewDisplayConfigArray; + } + } + + /** + * Alter the view display config. + * + * @param string $bundle + * The bundle. + */ + public function alterViewDisplayConfig(string $bundle): void { + $this->reporter->reportInfo("Altering view display config for field {$this->fieldName} on bundle {$bundle}..."); + $viewModeOptions = $this->getViewModeOptions($bundle); + foreach ($viewModeOptions as $viewMode => $options) { + $this->alterViewDisplayConfigForMode($bundle, $viewMode); + } + } + + /** + * Alter the view display config for a specific mode. + * + * @param string $bundle + * The bundle. + * @param string $viewMode + * The view mode. + */ + public function alterViewDisplayConfigForMode(string $bundle, string $viewMode): void { + $this->reporter->reportInfo("Altering view display config for field {$this->fieldName} on bundle {$bundle} for view mode {$viewMode}..."); + $savedConfig = $this->workingViewDisplayConfigs[$bundle][$viewMode]; + print_r($savedConfig); + $fieldName = $this->fieldName; + // If not present, then it is disabled and we don't need to care about it. + if (!isset($savedConfig[$fieldName])) { + return; + } + // We're just replacing the old settings, so this is basically a no-op. + $this->workingViewDisplayConfigs[$bundle][$viewMode] = $savedConfig; + } + + /** + * Update the view display config. + * + * @param string $bundle + * The bundle. + */ + public function updateViewDisplayConfig(string $bundle): void { + $this->reporter->reportInfo("Updating view display config for field {$this->fieldName} on bundle {$bundle}..."); + $viewModeOptions = $this->getViewModeOptions($bundle); + foreach ($viewModeOptions as $viewMode => $options) { + $this->updateViewDisplayConfigForMode($bundle, $viewMode, $this->workingViewDisplayConfigs[$bundle][$viewMode]['content']); + } + } + + /** + * Update the view display config for a specific mode. + * + * @param string $bundle + * The bundle. + * @param string $viewMode + * The view mode. + * @param array $config + * The config. + */ + public function updateViewDisplayConfigForMode(string $bundle, string $viewMode, array $config): void { + $this->reporter->reportInfo("Updating view display config for field {$this->fieldName} on bundle {$bundle} for view mode {$viewMode}..."); + $this->getEntityDisplayRepository() + ->getViewDisplay($this->entityType, $bundle, $viewMode) + ->setComponent($this->fieldName, $config) + ->save(); + } + + /** + * Rolls back a migration. + * + * @throws \Drupal\va_gov_live_field_migration\Exception\MigrationRollbackException + * If the migration rollback fails. + */ + public function rollback() { + throw new MigrationRollbackException('Not implemented.'); + } + + /** + * Verifies the migration. + * + * @throws \Drupal\va_gov_live_field_migration\Exception\MigrationVerificationException + * If the migration verification fails. + */ + public function verify() { + throw new MigrationVerificationException('Not implemented.'); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/MigratorInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/MigratorInterface.php new file mode 100644 index 0000000000..8cd1e83d56 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/MigratorInterface.php @@ -0,0 +1,34 @@ +getReporter()->reportInfo("Dropping table {$table}..."); + $this->getDatabase()->dropTable($table); + } + + /** + * {@inheritDoc} + */ + public function backupFieldTables(): void { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $this->getReporter()->reportInfo("Backing up tables for field {$fieldName} on entity {$entityType}..."); + $this->getDatabase()->backupPrimaryFieldTable($entityType, $fieldName); + $this->getDatabase()->backupFieldRevisionTable($entityType, $fieldName); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/EntityDisplayOperationsInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/EntityDisplayOperationsInterface.php new file mode 100644 index 0000000000..fd1267f1b4 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/EntityDisplayOperationsInterface.php @@ -0,0 +1,61 @@ +getEntityType(); + return $this->getEntityDisplayRepository()->getFormModeOptionsByBundle($entityType, $bundle); + } + + /** + * {@inheritDoc} + */ + public function getFormDisplayConfig(string $bundle, string $formMode): EntityFormDisplayInterface { + $entityType = $this->getEntityType(); + return $this->getEntityDisplayRepository()->getFormDisplay($entityType, $bundle, $formMode); + } + + /** + * {@inheritDoc} + */ + public function getViewModeOptions(string $bundle): array { + $entityType = $this->getEntityType(); + return $this->getEntityDisplayRepository()->getViewModeOptionsByBundle($entityType, $bundle); + } + + /** + * {@inheritDoc} + */ + public function getViewDisplayConfig(string $bundle, string $viewMode): EntityViewDisplayInterface { + $entityType = $this->getEntityType(); + return $this->getEntityDisplayRepository()->getViewDisplay($entityType, $bundle, $viewMode); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/FieldStorageOperationsInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/FieldStorageOperationsInterface.php new file mode 100644 index 0000000000..3f94fff0a9 --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/FieldStorageOperationsInterface.php @@ -0,0 +1,68 @@ +getEntityType(); + $fieldName = $this->getFieldName(); + $this->reporter->reportInfo("Verifying field {$fieldName} on entity {$entityType}..."); + $fieldStorageDefinition = $this->getEntityFieldManager()->getFieldStorageDefinitions($entityType)[$fieldName]; + if ($fieldStorageDefinition == NULL) { + throw new MigrationFieldNotFoundException("Field $fieldName not found on $entityType."); + } + if ($fieldStorageDefinition->getType() !== $fieldType) { + throw new MigrationFieldWrongTypeException("Field $fieldName on $entityType is not of type $fieldType."); + } + } + + /** + * {@inheritDoc} + */ + public function getFieldStorageConfig(): FieldStorageConfigInterface { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $fieldStorageConfigStorage = $this->getEntityTypeManager()->getStorage('field_storage_config'); + $fieldStorageConfig = $fieldStorageConfigStorage->load($entityType . '.' . $fieldName); + return $fieldStorageConfig; + } + + /** + * {@inheritDoc} + */ + public function getFieldConfig(string $bundle): FieldConfigInterface { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $fieldConfigStorage = $this->getEntityTypeManager()->getStorage('field_config'); + $fieldConfig = $fieldConfigStorage->load($entityType . '.' . $bundle . '.' . $fieldName); + return $fieldConfig; + } + + /** + * {@inheritDoc} + */ + public function getFieldBundles(): array { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $fieldStorageConfig = $this->getFieldStorageConfig($entityType, $fieldName); + return $fieldStorageConfig->getBundles(); + } + + /** + * {@inheritDoc} + */ + public function deleteFieldStorageConfig(): void { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $this->reporter->reportInfo("Deleting field storage config for field {$fieldName} on entity {$entityType}..."); + $fieldStorageConfig = $this->getFieldStorageConfig($entityType, $fieldName); + $fieldStorageConfig->delete(); + } + + /** + * {@inheritDoc} + */ + public function createFieldStorageConfig(array $fieldStorageConfig): void { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $this->reporter->reportInfo("Creating field storage config for field {$fieldName} on entity {$entityType}..."); + $fieldStorageConfigStorage = $this->getEntityTypeManager()->getStorage('field_storage_config'); + $fieldStorageConfig = $fieldStorageConfigStorage->create($fieldStorageConfig); + $fieldStorageConfig->save(); + } + + /** + * {@inheritDoc} + */ + public function createFieldConfigs(array $fieldConfigs): void { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $this->reporter->reportInfo("Creating field configs for field {$fieldName} on entity {$entityType}..."); + $fieldConfigStorage = $this->getEntityTypeManager()->getStorage('field_config'); + foreach ($fieldConfigs as $fieldConfig) { + $fieldConfig = $fieldConfigStorage->create($fieldConfig); + $fieldConfig->save(); + } + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/MigrationStatusInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/MigrationStatusInterface.php new file mode 100644 index 0000000000..995df37a5f --- /dev/null +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Migrator/Traits/MigrationStatusInterface.php @@ -0,0 +1,54 @@ +getEntityType(); + $fieldName = $this->getFieldName(); + $status = $this->getState()->getStatus($this->getPluginId(), $entityType, $fieldName); + if (!$status) { + $status = $this->getState()->createStatus($this->getPluginId(), $entityType, $fieldName); + } + return $status; + } + + /** + * {@inheritDoc} + */ + public function setMigrationStatusObject(StatusInterface $status): void { + $this->getState()->setStatus($status); + } + + /** + * {@inheritDoc} + */ + public function deleteMigrationStatusObject(): void { + $entityType = $this->getEntityType(); + $fieldName = $this->getFieldName(); + $this->getState()->deleteStatus($this->getPluginId(), $entityType, $fieldName); + } + + /** + * {@inheritDoc} + */ + public function getMigrationStatus(): string { + return $this->getMigrationStatusObject()->getStatus(); + } + + /** + * {@inheritDoc} + */ + public function updateMigrationStatus(string $status): void { + $statusObject = $this->getMigrationStatusObject(); + $statusObject->setStatus($status); + $this->setMigrationStatusObject($statusObject); + } + + /** + * {@inheritDoc} + */ + public function deleteMigrationStatus(): void { + $this->deleteMigrationStatusObject(); + } + +} diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php index 825c98debb..c326e5e47d 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/FieldProvider/Issue14995.php @@ -105,6 +105,9 @@ public static function create( */ public function isStillValid(string $entityType, string $fieldName) : bool { $fieldStorage = $this->entityFieldManager->getFieldStorageDefinitions($entityType)[$fieldName]; + if ($fieldStorage === NULL) { + return FALSE; + } return in_array($fieldStorage->getType(), static::VALID_FIELD_TYPES); } @@ -122,8 +125,8 @@ public function isStillValid(string $entityType, string $fieldName) : bool { * Whether this field exists on a specified bundle. */ public function fieldExistsOnBundle(string $entityType, string $fieldName, string $bundle) : bool { - $fieldMap = $this->entityFieldManager->getFieldMapByFieldType($entityType, $fieldName); - return in_array($bundle, array_keys($fieldMap)); + $fieldConfig = $this->entityFieldManager->getFieldDefinitions($entityType, $bundle)[$fieldName]; + return $fieldConfig !== NULL; } /** @@ -132,7 +135,7 @@ public function fieldExistsOnBundle(string $entityType, string $fieldName, strin public function getFields(string $entityType, string $bundle = NULL) : array { $fields = static::FIELDS[$entityType] ?? []; if ($bundle !== NULL) { - $fields = array_filter($fields, function ($field) use ($bundle) { + $fields = array_filter($fields, function ($field) use ($entityType, $bundle) { return $this->fieldExistsOnBundle($entityType, $field, $bundle); }); } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/StringToStringLong.php b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/StringToStringLong.php index de1056e1f1..d33779e231 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/StringToStringLong.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/StringToStringLong.php @@ -2,9 +2,6 @@ namespace Drupal\va_gov_live_field_migration\Plugin\Migration; -use Drupal\va_gov_live_field_migration\Exception\MigrationErrorException; -use Drupal\va_gov_live_field_migration\Exception\MigrationRollbackException; -use Drupal\va_gov_live_field_migration\Exception\MigrationVerificationException; use Drupal\va_gov_live_field_migration\Migration\Plugin\MigrationPluginBase; /** @@ -21,21 +18,24 @@ class StringToStringLong extends MigrationPluginBase { * {@inheritDoc} */ public function runMigration(string $entityType, string $fieldName) : void { - throw new MigrationErrorException('Not implemented.'); + $migrator = $this->migratorFactory->getStringToStringLongMigrator($entityType, $fieldName); + $migrator->run(); } /** * {@inheritDoc} */ public function rollbackMigration(string $entityType, string $fieldName) : void { - throw new MigrationRollbackException('Not implemented.'); + $migrator = $this->migratorFactory->getStringToStringLongMigrator($entityType, $fieldName); + $migrator->rollback(); } /** * {@inheritDoc} */ public function verifyMigration(string $entityType, string $fieldName) : void { - throw new MigrationVerificationException('Not implemented.'); + $migrator = $this->migratorFactory->getStringToStringLongMigrator($entityType, $fieldName); + $migrator->verify(); } } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TestException.php b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TestException.php index a5ef5e393c..dd1926ac19 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TestException.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TestException.php @@ -2,8 +2,8 @@ namespace Drupal\va_gov_live_field_migration\Plugin\Migration; -use Drupal\va_gov_live_field_migration\Exception\MigrationErrorException; use Drupal\va_gov_live_field_migration\Exception\MigrationRollbackException; +use Drupal\va_gov_live_field_migration\Exception\MigrationRunException; use Drupal\va_gov_live_field_migration\Exception\MigrationVerificationException; use Drupal\va_gov_live_field_migration\Migration\Plugin\MigrationPluginBase; @@ -23,7 +23,7 @@ class TestException extends MigrationPluginBase { * {@inheritDoc} */ public function runMigration(string $entityType, string $fieldName) : void { - throw new MigrationErrorException('This is a test exception.'); + throw new MigrationRunException('This is a test exception.'); } /** diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php index 9f6b825e65..3c191060ea 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/Plugin/Migration/TextToStringLong.php @@ -2,9 +2,6 @@ namespace Drupal\va_gov_live_field_migration\Plugin\Migration; -use Drupal\va_gov_live_field_migration\Exception\MigrationErrorException; -use Drupal\va_gov_live_field_migration\Exception\MigrationRollbackException; -use Drupal\va_gov_live_field_migration\Exception\MigrationVerificationException; use Drupal\va_gov_live_field_migration\Migration\Plugin\MigrationPluginBase; /** @@ -21,21 +18,24 @@ class TextToStringLong extends MigrationPluginBase { * {@inheritDoc} */ public function runMigration(string $entityType, string $fieldName) : void { - throw new MigrationErrorException('Not implemented.'); + $migrator = $this->migrationFactory->getTextToStringLongMigrator($entityType, $fieldName); + $migrator->run(); } /** * {@inheritDoc} */ public function rollbackMigration(string $entityType, string $fieldName) : void { - throw new MigrationRollbackException('Not implemented.'); + $migrator = $this->migrationFactory->getTextToStringLongMigrator($entityType, $fieldName); + $migrator->rollback(); } /** * {@inheritDoc} */ public function verifyMigration(string $entityType, string $fieldName) : void { - throw new MigrationVerificationException('Not implemented.'); + $migrator = $this->migrationFactory->getTextToStringLongMigrator($entityType, $fieldName); + $migrator->verify(); } } diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/State/State.php b/docroot/modules/custom/va_gov_live_field_migration/src/State/State.php index 4b2c08d5ad..1689e382ad 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/State/State.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/State/State.php @@ -42,6 +42,15 @@ public function getStatus(string $migrationId, string $entityType, string $field return Status::fromJson($status); } + /** + * {@inheritDoc} + */ + public function createStatus(string $migrationId, string $entityType, string $fieldName): StatusInterface { + $status = new Status($migrationId, $entityType, $fieldName); + $this->setStatus($status); + return $this->getStatus($migrationId, $entityType, $fieldName); + } + /** * {@inheritDoc} */ diff --git a/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php b/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php index f363e26b9f..5b90b0f4ec 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php +++ b/docroot/modules/custom/va_gov_live_field_migration/src/State/StateInterface.php @@ -27,6 +27,21 @@ interface StateInterface { */ public function getStatus(string $migrationId, string $entityType, string $fieldName): StatusInterface; + /** + * Create a status object for the specified migration. + * + * @param string $migrationId + * The migration ID. + * @param string $entityType + * The entity type. + * @param string $fieldName + * The field name. + * + * @return \Drupal\va_gov_live_field_migration\Migration\Status\StatusInterface + * A new object containing information about the migration. + */ + public function createStatus(string $migrationId, string $entityType, string $fieldName): StatusInterface; + /** * Set information about the specified migration. * diff --git a/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml b/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml index 33d84d11a5..831d1a2b5f 100644 --- a/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml +++ b/docroot/modules/custom/va_gov_live_field_migration/va_gov_live_field_migration.services.yml @@ -5,10 +5,17 @@ services: plugin.manager.va_gov_live_field_migration.migration: class: Drupal\va_gov_live_field_migration\Migration\Plugin\MigrationPluginManager parent: default_plugin_manager + va_gov_live_field_migration.database: + class: Drupal\va_gov_live_field_migration\Database\Database + arguments: + - '@database' va_gov_live_field_migration.field_provider_resolver: class: Drupal\va_gov_live_field_migration\FieldProvider\Resolver\Resolver arguments: - '@plugin.manager.va_gov_live_field_migration.field_provider' + va_gov_live_field_migration.field_purger: + class: Drupal\va_gov_live_field_migration\FieldPurger\FieldPurger + arguments: [] va_gov_live_field_migration.migration_resolver: class: Drupal\va_gov_live_field_migration\Migration\Resolver\Resolver arguments: @@ -21,6 +28,18 @@ services: - '@va_gov_live_field_migration.migration_resolver' - '@va_gov_live_field_migration.reporter' - '@va_gov_live_field_migration.state' + va_gov_live_field_migration.migrator_factory: + class: Drupal\va_gov_live_field_migration\Migrator\Factory\Factory + arguments: + - '@string_translation' + - '@va_gov_live_field_migration.reporter' + - '@va_gov_live_field_migration.state' + - '@va_gov_live_field_migration.database' + - '@entity_display.repository' + - '@entity_field.manager' + - '@entity_type.manager' + - '@entity_type.repository' + - '@va_gov_live_field_migration.field_purger' va_gov_live_field_migration.reporter: class: Drupal\va_gov_live_field_migration\Reporter\Reporter arguments: ['@messenger', '@logger.factory']