Problem/Motivation

      catch (MigrateException $e) {
        $this->migration->getIdMap()->saveIdMapping($row, [], $e->getStatus());
        $this->saveMessage($e->getMessage(), $e->getLevel());
        $save = FALSE;
      }
      catch (MigrateSkipRowException $e) {
        if ($e->getSaveToMap()) {
          $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_IGNORED);
        }
        if ($message = trim($e->getMessage())) {
          $this->saveMessage($message, MigrationInterface::MESSAGE_INFORMATIONAL);
        }
        $save = FALSE;
      }

This code in MigrateExecutable catches exceptions, typically from process plugins, and puts the message into the migration's message log. But typically, process plugins throw exceptions with messages that don't tell you enough about what caused the problem. When you find the message in the map table, you know the row, and with some grepping of the message you can find the place in the code that caused the exception, but you don't know:

  • the actual migration (since a migration lookup process could have caused the problem)
  • the destination property (the same process plugin could be in use in several destination properties).

We could say that all process plugins should put that in their exception messages, but that's not very good DX as it needlessly repeats code.

Instead, MigrateExecutable should prepend these messages with extra detail.

Steps to reproduce

Proposed resolution

Add migration id and destination property to the exception message so it is in this format:

migration_id: destination_property: message

Example message:
d7_field_instance:type: Can't migrate source field field_text_long_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-d...

Change any process plugin that is passing the destination property in MigrateException or MigrateSkipRowException

Remaining tasks

Patch
Review
Commit

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#76 2976098-76.patch32.91 KBquietone
#76 interdiff-74-76.txt2.16 KBquietone
#74 2976098-74.patch31.33 KBquietone
#74 interdiff-71-74.txt709 bytesquietone
#71 2976098-71.patch31.33 KBquietone
#71 interdiff-70-71.txt547 bytesquietone
#70 2976098-70.patch31.65 KBquietone
#70 interdiff-66-70.txt8.75 KBquietone
#66 2976098-66.patch27.37 KBquietone
#66 diff-64-66.txt4.66 KBquietone
#64 2976098-64.patch27.37 KBquietone
#64 diff-58-64.txt2 KBquietone
#57 2976098-58.patch28.43 KBquietone
#57 interdiff-56-58.txt529 bytesquietone
#56 diff-54-56.txt1.4 KBquietone
#54 2976098-54.patch28.38 KBquietone
#54 diff-53-54.txt813 bytesquietone
#53 2976098-53.patch28.38 KBquietone
#53 interdiff-51-52.txt710 bytesquietone
#51 2976098-51.patch28.38 KBquietone
#51 interdiff-50-51.txt715 bytesquietone
#50 2976098-50.patch28.39 KBquietone
#50 interdiff-48-50.txt9.35 KBquietone
#48 2976098-48.patch22.32 KBquietone
#48 interdiff-46-48.txt429 bytesquietone
#46 2976098-46.patch22.4 KBquietone
#46 interdiff-41-47.txt4.14 KBquietone
#41 2976098-41.patch18.95 KBquietone
#41 interdff-40-41.txt1016 bytesquietone
#40 2976098-40.patch19.21 KBquietone
#40 interdiff-38-40.txt2.97 KBquietone
#39 2976098-38.patch19.22 KBalexpott
#39 37-38-interdiff.txt557 bytesalexpott
#38 2976098-37.patch19.52 KBalexpott
#38 34-37-interdiff.txt6.18 KBalexpott
#37 2976098-37.patch15.53 KBquietone
#37 interdiff-35-37.txt2.62 KBquietone
#35 2976098-35.patch16.96 KBquietone
#35 interdiff-34-35.txt2.65 KBquietone
#34 2976098-34.patch15 KBquietone
#34 interdiff-25-34.txt1.34 KBquietone
#33 2976098-25.patch16.79 KBquietone
#33 interdiff-25-33.txt1.34 KBquietone
#32 2976098-32.patch18.75 KBquietone
#32 interdiff-25-32.txt2.65 KBquietone
#31 2976098-36.patch18.95 KBquietone
#31 interdiff-25-31.txt2.89 KBquietone
#25 2976098-25.patch16.79 KBquietone
#25 interdiff-23-25.txt903 bytesquietone
#23 2976098-23.patch16.77 KBquietone
#23 interdiff-17-23.txt7.34 KBquietone
#17 2976098-17.patch9.95 KBsivaji_ganesh_jojodae
#17 interdiff.txt967 bytessivaji_ganesh_jojodae
#13 2976098-13.patch1.87 KBquietone
#15 2976098-15.patch9.3 KBquietone
#11 2976098-11.patch3.87 KBquietone
#15 interdiff-13-15.txt6.79 KBquietone
#56 2976098-56.patch28.42 KBquietone

Comments

joachim created an issue. See original summary.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

wim leers’s picture

Category: Bug report » Task
Priority: Normal » Major

This is absolutely still an issue. I've been looking into the migration system for about a week, and I already lost count how many times I set a breakpoint in that place and then had to wait patiently for the migration to finally reach that point. Better logging could be a massive productivity boost.

wim leers’s picture

wim leers’s picture

wim leers’s picture

Priority: Major » Normal

I think we should bump #2959444: [Meta] Improve exception messages in process plugins to major instead of this. I only found that meta now.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

benjifisher’s picture

Issue summary: View changes

+1 for adding information when the exception is caught, not when it is thrown.

I am making some minor edits to the issue summary.

quietone’s picture

Status: Active » Needs review
StatusFileSize
new3.87 KB

A possible solution. Here is an example of the message, the result of testing with the extract process plugin
Migration 'default_language', destination 'default_langcode' Array index missing, extraction failed.

Status: Needs review » Needs work

The last submitted patch, 11: 2976098-11.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new1.87 KB

This will be better.

Status: Needs review » Needs work

The last submitted patch, 13: 2976098-13.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new6.79 KB
new9.3 KB

Adjust the tests that make assertions on the migrate message.

Status: Needs review » Needs work

The last submitted patch, 15: 2976098-15.patch, failed testing. View results

sivaji_ganesh_jojodae’s picture

Status: Needs work » Needs review
StatusFileSize
new967 bytes
new9.95 KB

Patch attached tries to fix the last test error.

quietone’s picture

@DevJoJodae, thanks for making a patch. Sadly, we have duplicated work, that is I have made the same patch when I returned from dinner. Please have a look at the recent comments to figure out if someone is actively working on it so we can avoid this in the future. Thx.

benjifisher’s picture

benjifisher’s picture

Assigned: Unassigned » benjifisher
Issue tags: +Global2020

Surprisingly, the patch in #17 does not conflict with the current patch on #2969551: Migrate messages from caught exceptions need file and line details. (That issue has the same patch attached in #12 and #18.)

I will review this issue today, as part of DrupalCon Global.

benjifisher’s picture

Assigned: benjifisher » Unassigned
Status: Needs review » Needs work
  1. This new property needs an @var comment.

     +++ b/core/modules/migrate/src/MigrateExecutable.php
     @@ -90,6 +90,8 @@ class MigrateExecutable implements MigrateExecutableInterface {
         */
        public $message;
    
     +  protected $destination;
  2. Can we be more consistent here?

     @@ -205,7 +207,9 @@ public function import() {
            }
            catch (MigrateException $e) {
              $this->getIdMap()->saveIdMapping($row, [], $e->getStatus());
     -        $this->saveMessage($e->getMessage(), $e->getLevel());
     +        $migration_id = $this->migration->getPluginId();
     +        $msg = "$migration_id:$this->destination: " . $e->getMessage();
     +        $this->saveMessage($msg, $e->getLevel());
              $save = FALSE;
            }
            catch (MigrateSkipRowException $e) {
     @@ -213,7 +217,9 @@ public function import() {
                $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_IGNORED);
              }
              if ($message = trim($e->getMessage())) {
     -          $this->saveMessage($message, MigrationInterface::MESSAGE_INFORMATIONAL);
     +          $migration_id = $this->migration->getPluginId();
     +          $msg = "$migration_id:$this->destination: " . $e->getMessage();
     +          $this->saveMessage($msg, MigrationInterface::MESSAGE_INFORMATIONAL);
              }

    If it were a few more lines, or if we did the same thing a third time, then I would want to add a helper function to be more DRY. Either of those might happen in the future. If we can be a little more consistent now, it will help when we decide to add that helper function.

    We should also get rid of the test for an empty message. Since we have useful information (the destination property and the current migration) we should save a message even if $e->getMessage() is empty.

    I wonder if we should use sprintf() instead of variables in double quotes. We are playing with messages from exception messages, and sprintf() is recommended there. But maybe this counts as a "foolish consistency". (As in the often misquoted "A foolish consistency is the hobgoblin of little minds.")

  3. The updates to the tests are all straightforward. I think this means that our test coverage was already pretty good, and we just need to make the necessary adjustments when changing the messages.

  4. We do not need redundant information. Most process plugins do not include $destination_property when they throw a MigrateException, but FormatDate, ImageStyleMappings, and StaticMap do. We can handle this in a follow-up issue if you prefer, but I think it is not too hard to do it as part of this issue. Curiously, BlockVisibility passes destination_property to sprintf(), but there is no matching %s. We should clean that up at the same time.

benjifisher’s picture

I also did some manual testing. I applied the patch to a recent project (Drupal 8.9.2) and tested drush mim d7_file --upgrade. As expected, drush mmsg d7_file shows a lot of messages like this:

d7_file:uri: File '...' does not exist
quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new7.34 KB
new16.77 KB

#21
1. Fixed
2. Improved and using sprintf
3. Nice
4. Fixed.

Good to know that manual testing shows this works.

Status: Needs review » Needs work

The last submitted patch, 23: 2976098-23.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new903 bytes
new16.79 KB

I was wondering if removing the test for empty messages was going to cause an error (#21.2) and it does. Restoring that test.

benjifisher’s picture

Status: Needs review » Needs work

I do not have time to look at it now, but this might be a case where we should fix the test instead fixing the "bug".

quietone’s picture

Status: Needs work » Needs review

I've only got a moment...

If we change this then every MigrateSkipRowException will generate messages causing a lot of noise in the message tables. I think that is out of scope and we should discuss in another issue.

benjifisher’s picture

Status: Needs review » Reviewed & tested by the community

@quietone:

You convinced me! That part of #21.2 was at best out of scope. Probably it was simply a bad idea.

The rest of the changes look great. The additional changes to the tests after implementing #21.4 look good. Once again, it looks as though our test coverage is in pretty good shape.

catch’s picture

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -213,7 +221,8 @@ public function import() {
@@ -361,6 +370,7 @@ protected function getIdMap() {

@@ -361,6 +370,7 @@ protected function getIdMap() {
    */
   public function processRow(Row $row, array $process = NULL, $value = NULL) {
     foreach ($this->migration->getProcessPlugins($process) as $destination => $plugins) {
+      $this->destination = $destination;
       $multiple = FALSE;
       /** @var $plugin \Drupal\migrate\Plugin\MigrateProcessInterface */

Is it really OK/necessary to keep overwriting $this->destination here in the foreach loop? I think it needs a comment if it is.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Following up @catch's question...

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -205,7 +212,8 @@ public function import() {
-        $this->saveMessage($e->getMessage(), $e->getLevel());
+        $msg = sprintf("%s:%s: %s", $this->migration->getPluginId(), $this->destination, $e->getMessage());
+        $this->saveMessage($msg, $e->getLevel());

@@ -213,7 +221,8 @@ public function import() {
-          $this->saveMessage($message, MigrationInterface::MESSAGE_INFORMATIONAL);
+          $msg = sprintf("%s:%s: %s", $this->migration->getPluginId(), $this->destination, $e->getMessage());
+          $this->saveMessage($msg, MigrationInterface::MESSAGE_INFORMATIONAL);

Get we use $destination->getPluginId() here instead of $this->destination and not add the property?

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new2.89 KB
new18.95 KB

29. Changed the foreach to set $this->destination.
30. No, can't do that, sorry. Here $destination is the current destination property name on the process pipeline not a destination plugin.

quietone’s picture

StatusFileSize
new2.65 KB
new18.75 KB

Ignore previous patch, bad patch and numbered wrong too!

29. Changed the foreach to set $this->destination.
30. No, can't do that, sorry. Here $destination is the current destination property name on the process pipeline not a destination plugin.

quietone’s picture

StatusFileSize
new1.34 KB
new16.79 KB

Seems my cold is affecting me in more ways than I thought.

Restarting from reroll of patch #25.

quietone’s picture

StatusFileSize
new1.34 KB
new15 KB

Can this get any worse?

This is just a reroll

quietone’s picture

StatusFileSize
new2.65 KB
new16.96 KB

Now add the changes I tried to do way back in #31.

#29. Changed the foreach to set $this->destination.
#30. No, can't do that, sorry. Here $destination is the current destination property name on the process pipeline not a destination plugin.

Status: Needs review » Needs work

The last submitted patch, 35: 2976098-35.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new2.62 KB
new15.53 KB

Right, that won't work with the sub_process process plugin. How about adding a catch, saving the destination and throwing the exception.

alexpott’s picture

StatusFileSize
new6.18 KB
new19.52 KB

oops x-post with #37 - I still think this approach is preferable.

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -90,6 +90,13 @@ class MigrateExecutable implements MigrateExecutableInterface {
+  /**
+   * The destination property name.
+   *
+   * @var string
+   */
+  protected $destination;

@@ -360,7 +369,7 @@ protected function getIdMap() {
-    foreach ($this->migration->getProcessPlugins($process) as $destination => $plugins) {

I don't think assignment like this works.

Plus there are way too many things called $destination here it's confusing. And adding it as a class property makes it stateful and look more useful than it is.

Here's an alternate solution that allows us to do this without the class property with a small amount of refactoring that also makes the code a bit simpler to read. Also it makes it obvious that at the earliest point an exception might thrown in $this->migration->getProcessPlugins($process) we might not get have a destination property name.

Interdiff is back to #34 since that is the last working version.

alexpott’s picture

StatusFileSize
new557 bytes
new19.22 KB

Lol forgot to remove the destination class property which was one of the aims - oops.

quietone’s picture

StatusFileSize
new2.97 KB
new19.21 KB

@alexpott, Yes, that is better. Thanks.

This is some changes to comments.

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -361,56 +366,77 @@ protected function getIdMap() {
+   *   (optional) Initial value of the pipeline for the first destination.
+   *   Usually setting this is not necessary as $process typically starts with
+   *   a 'get'. This is useful only when the $process contains a single
+   *   destination and needs to access a value outside of the source. See
+   *   \Drupal\migrate\Plugin\migrate\process\SubProcess::transformKey for an
+   *   example.

This needs to be modified as well but maybe tomorrow. $process isn't used here. It is from the doc block for proccesRow in MigrateExecutableInterface and references $process which isn't used here.

quietone’s picture

StatusFileSize
new1016 bytes
new18.95 KB

Simplify the documentation for the $value parameter, that is, don't repeat what is elsewhere, and add an @see.

wim leers’s picture

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -199,13 +199,17 @@ public function import() {
+        $msg = sprintf("%s:%s: %s", $this->migration->getPluginId(), $destination_property_name, $e->getMessage());
+        $this->saveMessage($msg, $e->getLevel());
Instead, MigrateExecutable should prepend these messages with extra detail.

Why prepend structured information to an unstructured string/blob?

This is structured data. If this were saved as separate fields (migration_plugin_id and destination_property_name) in the migrate_message_* DB tables, then this would be much easier to search.

It'd also open the door for a single migrate_messages table, to allow searching all migration messages with a single query, rather than dozens (or even hundreds) of migration_message_* tables.

So IMHO this is doing the right thing, but in the wrong way.

AFAICT this is even literally repeating a subset of the table name (the asterisk in migration_message_*) for every row in that table? 🤭

mikelutz’s picture

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -199,13 +206,17 @@ public function import() {
       try {
...
+        foreach ($this->migration->getProcessPlugins() as $destination_property_names => $plugins) {
+          $this->processPipeline($row, $destination_property_names, $plugins, NULL);
+        }
         $save = TRUE;
       }
       catch (MigrateException $e) {
         $this->getIdMap()->saveIdMapping($row, [], $e->getStatus());
-        $this->saveMessage($e->getMessage(), $e->getLevel());
+        $msg = sprintf("%s:%s: %s", $this->migration->getPluginId(), $destination_property_names, $e->getMessage());
+        $this->saveMessage($msg, $e->getLevel());
         $save = FALSE;
       }

I don't know that we should include the potential exceptions thrown by ->getProcessPlugins() here with other migrate exceptions thrown when processing a row. That method doesn't depend on $row at all, so if it throws an exception (MigrateException or PluginException) it's going to continue to throw that exception for every row. I think if we catch an exception in that method, we should handle it separately and bow out of the migration.

mikelutz’s picture

Status: Needs review » Needs work

In lieu of a response suggesting otherwise, back to NW for #43

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new4.14 KB
new22.4 KB

Addressing #43. Looks to me like the current behavior is that if $this->migration->getProcessPlugins() throws an exception one will be thrown for every row. So, this changes that, for the better.

Status: Needs review » Needs work

The last submitted patch, 46: 2976098-46.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new429 bytes
new22.32 KB

I meant to remove those lines. This should be better.

Status: Needs review » Needs work

The last submitted patch, 48: 2976098-48.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new9.35 KB
new28.39 KB

That leaves failing tests of MigrateExecutable. The unit test needed lots of method calls removed and the Kernel test found errors in the $return value. Should be sorted now.

quietone’s picture

Issue summary: View changes
StatusFileSize
new715 bytes
new28.38 KB

Remove use of deprecated method,

Status: Needs review » Needs work

The last submitted patch, 51: 2976098-51.patch, failed testing. View results

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new710 bytes
new28.38 KB

I stray 'x' got into the file after I tested and while I was making the patch.

quietone’s picture

StatusFileSize
new813 bytes
new28.38 KB

Just a reroll

joachim’s picture

Admittedly I tried applying the patch here which is for 9.2 to 8.9 (!!!) and ignored what looked like a minor patch hunk that failed, but I get this error when I try to run a migration:

 [error]  TypeError: Argument 1 passed to Drupal\migrate\MigrateExecutable::doImport() must implement interface Drupal\migrate\Plugin\MigrateSourceInterface, instance of Drupal\migrate_tools\SourceFilter given, called in /Users/joachim/Sites/training-cloud/web/core/modules/migrate/src/MigrateExecutable.php on line 216 in Drupal\migrate\MigrateExecutable->doImport() (line 232 of /Users/joachim/Sites/training-cloud/web/core/modules/migrate/src/MigrateExecutable.php) #0 /Users/joachim/Sites/training-cloud/web/core/modules/migrate/src/MigrateExecutable.php(216): Drupal\migrate\MigrateExecutable->doImport(Object(Drupal\migrate_tools\SourceFilter), Array)
#1 /Users/joachim/Sites/training-cloud/vendor/drush/drush/includes/drush.inc(206): Drupal\migrate\MigrateExecutable->import()
#2 /Users/joachim/Sites/training-cloud/vendor/drush/drush/includes/drush.inc(197): drush_call_user_func_array(Array, Array)
#3 /Users/joachim/Sites/training-cloud/web/modules/contrib/migrate_tools/src/Commands/MigrateToolsCommands.php(846): drush_op(Array)
#4 [internal function]: Drupal\migrate_tools\Commands\MigrateToolsCommands->executeMigration(Object(Drupal\migrate\Plugin\Migration), 'ex_commerce_lin...', Array)
#5 /Users/joachim/Sites/training-cloud/web/modules/contrib/migrate_tools/src/Commands/MigrateToolsCommands.php(319): array_walk(Array, Array, Array)
#6 [internal function]: Drupal\migrate_tools\Commands\MigrateToolsCommands->import('ex_commerce_lin...', Array)
quietone’s picture

Issue summary: View changes
StatusFileSize
new1.4 KB
new28.42 KB

Rerolling the latest patch. And add some details to the IS>

quietone’s picture

StatusFileSize
new529 bytes
new28.43 KB

I wasn't on HEAD. Fix the coding standard error.

quietone’s picture

#55. When I rerolled the patch the change required was to add a use statement for Drupal\migrate\Plugin\MigrateSourceInterface in MigrateExecutable.

dinarcon’s picture

I am getting the same error described in #55 in a Drupal 9.1 installation. Migrate Tools 8.x-5.0 is installed which overrides the getSource method in its MigrateExecutable class. The method returns SourceFilter indeed.

Core's MigrateExecutable returns MigrateSourceInterface in its getSource implementation. This goes in line with the type hint in the doImport introduced by this patch.

quietone’s picture

AFAIKT, the return value from \Drupal\migrate_tools\MigrateExecutable::getSource is not in agreement with the parent class \Drupal\migrate\MigrateExecutable::getSource. How do we sort that out?

heddn’s picture

It could be the version of PHP

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -196,18 +195,56 @@ public function import() {
+  protected function doImport(MigrateSourceInterface $source, array $pipeline) {

For the purposes of this method, can we typehint on an iterator instead of the source interface? Probably not wise. See comments below.

The real solution is for the filter in migrate tools to get repaired. And maybe even in drush's new tooling? Looking at https://github.com/drush-ops/drush/blob/10.x/src/Drupal/Migrate/MigrateI..., it isn't quite the same thing. But it could suffer similar issues as we add typehinting. But that's another issue.

Suggested fix for Migrate Tools below:

<?php

namespace Drupal\migrate_tools;

use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
use Drupal\migrate\Plugin\MigrateSourceInterface;
use Drupal\migrate\Row;

/**
 * Class to filter source by an ID list.
 */
class SourceFilter extends \FilterIterator implements MigrateSourceInterface{

  /**
   * List of specific source IDs to import.
   *
   * @var array
   */
  protected $idList;

  /**
   * SourceFilter constructor.
   *
   * @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
   *   The ID map.
   * @param array $id_list
   *   The id list to use in the filter.
   */
  public function __construct(MigrateSourceInterface $source, array $id_list) {
    parent::__construct($source);
    $this->idList = $id_list;
  }

  /**
   * {@inheritdoc}
   */
  public function accept() {
    // No idlist filtering, don't filter.
    if (empty($this->idList)) {
      return TRUE;
    }
    // Some source plugins do not extend SourcePluginBase. These cannot be
    // filtered so warn and return all values.
    if (!$this->getInnerIterator() instanceof SourcePluginBase) {
      trigger_error(sprintf('The source plugin %s is not an instance of %s. Extend from %s to support idlist filtering.', $this->getInnerIterator()->getPluginId(), SourcePluginBase::class, SourcePluginBase::class));
      return TRUE;
    }
    // Row is included.
    if (in_array(array_values($this->getInnerIterator()->getCurrentIds()), $this->idList)) {
      return TRUE;
    }
  }

  public function fields() {
    return $this->getInnerIterator()->field();
  }

  public function prepareRow(Row $row) {
    return $this->getInnerIterator()->prepareRow($row);
  }

  public function __toString() {
    return $this->getInnerIterator()->__toString();
  }

  public function getIds() {
    return $this->getInnerIterator()->getIds();
  }

  public function getSourceModule() {
    return $this->getInnerIterator()->getSourceModule();
  }

  public function count() {
    return $this->getInnerIterator()->count();
  }

  public function getPluginId() {
    return $this->getInnerIterator()->getPluginId();
  }

  public function getPluginDefinition() {
    return $this->getInnerIterator()->getPluginDefinition();
  }

}

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Created an issue in Migrate Tools, #3212495: Change SourceFilter to implement MigrateSourceInterface and made a patch from #61.

quietone’s picture

StatusFileSize
new2 KB
new27.37 KB

A reroll.

joachim’s picture

Status: Needs review » Reviewed & tested by the community

LGTM

quietone’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new4.66 KB
new27.37 KB

Simple reroll to change order of parameters in assertEquals.

joachim’s picture

Status: Needs review » Reviewed & tested by the community
scotwith1t’s picture

I haven't nailed down the "why" behind this, just reporting. I wanted some more details from the migrate_message tables, came across and applied this patch (which applies cleanly to 9.2.3, btw). After applying, a migration that had been running smoothly started throwing the following:

TypeError: Drupal\migrate\MigrateExecutable::doImport(): Argument #1 ($source) must be of type Drupal\migrate\Plugin\MigrateSourceInterface, Drupal\migrate_tools\SourceFilter given, called in /var/www/html/web/core/modules/migrate/src/MigrateExecutable.php on line 214 in Drupal\migrate\MigrateExecutable->doImport() (line 230 of /var/www/html/web/core/modules/migrate/src/MigrateExecutable.php).

The quick fix for me was to simply remove the type-hinting for the $source from this new function from the patch
protected function doImport(MigrateSourceInterface $source, array $pipeline)

The source, thanks to the new and improved debugging :) seems to be a section in the migration's process section like this

  field_info_links:
    -
      plugin: callback
      callable: array_filter
      source:
        - field_custom_info_block_1
        - field_custom_info_block_2
        - field_custom_info_block_3
        - field_custom_link_block
    -
      plugin: callback
      unpack_source: true
      callable: array_merge
    -
      plugin: skip_on_empty
      method: process
    -
      plugin: sub_process
      process:
        temporary_ids:
          plugin: migration_lookup
          migration: mymigration_info_links
          source: value
          no_stub: TRUE
        target_id:
          plugin: extract
          source: '@temporary_ids'
          index:
            - 0
        target_revision_id:
          plugin: extract
          source: '@temporary_ids'
          index:
            - 1

I think, based on the feedback in the error, that it just doesn't handle the possibly empty value being passed around by something like array_fiilter? Or is there something inherently wrong with the way I've assembled the pipeline for this field and it's just rearing its head because this code is an improvement? All in all, though it caused this error (or caused mine to surface?), the improved logging was both super-helpful while simultaneously enabling me to track down why this new issue was arising. :)

quietone’s picture

+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -196,18 +195,56 @@ public function import() {
+   * Imports all rows for the give n pipeline.

typo
s/give n/given

quietone’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new8.75 KB
new31.65 KB

It is not as nice but this can be rearranged to not cause problems for MigrateTools.

quietone’s picture

StatusFileSize
new547 bytes
new31.33 KB

I was sure I had run commit-code-check.

joachim’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/migrate/src/MigrateExecutable.php
    @@ -196,88 +194,113 @@ public function import() {
    +    if ($pipeline) {
    

    I think this if is not necessary. And if it is necessary how come? Is a migration with no pipeline valid?

  2. +++ b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php
    @@ -94,7 +94,8 @@ public function testMessagesTeed() {
    -    $this->assertSame("source_message: 'a message' is not an array", reset($this->messages));
    +    $id = $this->migration->getPluginId();
    +    $this->assertSame(reset($this->messages), "source_message: $id:message: 'a message' is not an array");
    

    The first param of assertSame() should be the expected message. So this swapping around is incorrect.

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new709 bytes
new31.33 KB

1. A migration with an empty process pipeline can be created, the process just needs to be an array. For example,

id: test
label: test migration
source:
  plugin: embedded_data
  data_rows:
    -
      id: 1
      slogan: My site
  ids:
    id:
      type: string
  source_module: system
process: []
destination:
  plugin: config
  config_name: system.site
(9.3.x)$ ddev drush ms test
 ------------------- -------------- -------- ------- ---------- ------------- --------------------- 
  Group               Migration ID   Status   Total   Imported   Unprocessed   Last Imported        
 ------------------- -------------- -------- ------- ---------- ------------- --------------------- 
  Default (default)   test           Idle     1       0          1             2021-10-11 10:14:20  
 ------------------- -------------- -------- ------- ---------- ------------- --------------------- 
(9.3.x)$ ddev drush mim test
 [notice] Processed 0 items (0 created, 0 updated, 0 failed, 0 ignored) - done with 'test'

2. Fixed.

joachim’s picture

Status: Needs review » Reviewed & tested by the community
quietone’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new2.16 KB
new32.91 KB

I was doing a self review here when I noticed an unused variable.
+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -196,88 +194,113 @@ public function import() {
+ if ($message = trim($e->getMessage())) {

$message is not used. And further, I could not find a test of the catch block this is in. So, I wrote a test, actually just tagged a bit on to an existing kernel test.

joachim’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 8616edd and pushed to 9.3.x. Thanks!

  • alexpott committed 8616edd on 9.3.x
    Issue #2976098 by quietone, alexpott, Sivaji_Ganesh_Jojodae, joachim,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.