Problem/Motivation

#3061571: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up significantly improved migrations of text formats, which can be a big stumbling block when getting a migration off the ground.

I've been using the 8.8.x patch for months, been working fine.

Just updated to 9.0.x, and suddenly this is failing. Why?!

@heddn pointed out over at #3061571-19: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up:

+++ b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
@@ -207,6 +207,9 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
+        if ($key === NULL) {
+          continue;
+        }

This could use some docs on why this is needed. or split it out into another issue. I don't see any reason for its existence previously mentioned in this issue.

To which I responded over at #3061571-21: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up:

RE: SubProcess: I'd swear I needed to do this for stuff to work … but now it's been a month since I wrote that code and I stupidly didn't write down the "why" back then 🙈

So … let's remove it and let's see if it passes!

Well, turns out that is the root cause. Because this is what happens:

That error reads:

The "" plugin does not exist. Valid plugin IDs for Drupal\filter\FilterPluginManager are: editor_file_reference, filter_html_image_secure, filter_url, filter_null, filter_html, filter_autop, filter_align, filter_caption, filter_htmlcorrector, filter_html_escape, markdown, media_embed, TypogrifyFilter (/Users/wim.leers/Work/d8/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php:53)

… and it causes a text format to not get migrated at all! Which makes all content using that text format impossible to migrate too!

(My personal site uses the image_resize_filter module on Drupal 7, and this is what happens when I try to migrate it.)

Proposed resolution

Harden SubProcess.

Remaining tasks

  • Patch
  • Tests
  • Review

User interface changes

None.

API changes

TBD

Data model changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

Title: Follow-up for #3061571: » Follow-up for #3061571: harden SubProcess @MigrateProcessPlugin
Wim Leers’s picture

Issue summary: View changes
Wim Leers’s picture

Issue summary: View changes
heddn’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
@@ -207,6 +207,9 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
+        if ($key === NULL) {

Probably could use the "why" as a comment here.

Wim Leers’s picture

Huh, I did not mark this RTBC!!!

I cloned the issue using dreditor, which I never do. That must be why. Sorry about that! 😅

I tagged it Needs tests, so it definitely isn't RTBC yet!

quietone’s picture

Title: Follow-up for #3061571: harden SubProcess @MigrateProcessPlugin » Harden SubProcess process plugin
quietone’s picture

The last submitted patch, 8: 3126063-8-fail.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 8: 3126063-8.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs tests

The failure in Drupal\Tests\media_library\FunctionalJavascript\WidgetUploadTest is 100% unrelated. This is a random failure in JS tests in HEAD.

The *-fail patch in #8 failed with the following output:

1) Drupal\Tests\filter\Kernel\Migrate\d7\MigrateFilterFormatTest::testFilterFormat
The "" plugin does not exist. Valid plugin IDs for Drupal\filter\FilterPluginManager are: filter_url, filter_null, filter_html_image_secure, filter_html_escape, filter_htmlcorrector, filter_html, filter_caption, filter_autop, filter_align (/var/www/html/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php:53)
Failed asserting that false is true.

which is exactly what I originally reported. Therefore, the test that @quietone is indeed reproducing the problem! 🥳

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
@@ -207,6 +207,9 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
         }
+        if ($key === NULL) {
+          continue;
+        }
         $return[$key] = $destination;

Still need the why this might happen comment.

Wim Leers’s picture

#3153601: The "" plugin does not exist was marked as a duplicate of this.

@DamienMcKenna was highly active on that issue. Perhaps he can push this one across the finish line? 🤞😊

quietone’s picture

FWIW, The two tests cases are that 1) the process creates an id that does not exist or 2) the process creates an id that is NULL.

How that relates to real filter data, I am not sure. Looking at d7_filter_format it seems that module/delta are NULL? So, bad data?

DamienMcKenna’s picture

The patch in #8 resolved my problem with text filters. Here's a dump of the {filter} table, if it'd help anyone work out what's causing the problem.

quietone’s picture

Status: Needs work » Needs review
FileSize
887 bytes
6.93 KB

@DamienMcKenna, thank you. I do find it helpful to have real data, not just the test fixture, to look at.

This adds a comment.

DamienMcKenna’s picture

Status: Needs review » Reviewed & tested by the community

RTBC from me, though I think the last line should be "@see \Drupal\filter\Plugin\migrate\process\FilterID"?

DamienMcKenna’s picture

Running this on another 8.9.2 codebase (yay upgrading D7 sites!) I now end up with this error:

Missing filter plugin: filter_null

Is this related?

quietone’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
801 bytes
6.93 KB

Yes, it should be @see.

Re #18. It is not really related to this issue. The Known Issues page has a section on Text/Input formats which discusses filter_null. Hopefully, that will answer your query.

DamienMcKenna’s picture

Status: Needs review » Reviewed & tested by the community

The latest patch looks good, thank you.

As for filter_null, I didn't realize it was going to throw an error, so I'll look into it; thank you.

joseph.olstad’s picture

patch 19 also applies cleanly to 8.8.x, 8.9.x and 9.1.x
although I haven't yet tested it, next time I get the plugin missing glitch I will test.

benjifisher’s picture

I compared the patch in #19 to the one in #12, which was previously RTBC. The later patch adds the comment

        // Do not save the result if the key is NULL. The configured process
        // pipeline used in transformKey() will return NULL if a
        // MigrateSkipProcessException is thrown.
        // @see \Drupal\filter\Plugin\migrate\process\FilterID

in the place indicated in #12.

I do not think it is worth setting the status back to NW, but

        if ($key === NULL) {
          continue;
        }
        $return[$key] = $destination;

at the end of the loop is a little more complicated than

        if ($key !== NULL) {
          $return[$key] = $destination;
        }

and, in the unit test, this looks redundant:

    $this->assertCount(0, $new_value);
    $this->assertArrayEquals([], $new_value);
quietone’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
1.5 KB
6.93 KB

Fixes for #22.

benjifisher’s picture

Status: Needs review » Reviewed & tested by the community

@quietone:

Thanks for indulging me! The patch in #23 addresses the two tiny issues I pointed out in #22.

Back to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
@@ -207,7 +207,13 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
-        $return[$key] = $destination;
+        // Do not save the result if the key is NULL. The configured process
+        // pipeline used in transformKey() will return NULL if a
+        // MigrateSkipProcessException is thrown.
+        // @see \Drupal\filter\Plugin\migrate\process\FilterID
+        if ($key !== NULL) {
+          $return[$key] = $destination;
+        }

I've stared at this code many times trying to work out if this is correct. I've considered what happens if the sub process whats to set a null value and isn't the call above

        $migrate_executable->processRow($new_row, $this->configuration['process']);
        $destination = $new_row->getDestination();

Also generating a MigrateSkipProcessException? (I believe it is)

So maybe we should change \Drupal\migrate\MigrateExecutable::processRow() to have a mode when it does not catch exceptions and then we could handle the skip row here for better consistency.

However the current solution if okay. If the key transformation returns NULL then this means that it should be unset. I think this should be properly documented in the plugin documentation. In this part:

 * Normally the array returned from sub_process will have its original keys. If
 * you need to change the key, it is possible for the returned array to be keyed
 * by one of the transformed values in the sub-array. For the same source data
 * used in the previous example, the migration below would result to keys
 * 'filter_2' and 'filter_0'.

Something like, If the key is transformed to a NULL then the value will be omitted. Or something like that... I'm not too happy with the end of this but something like that.

quietone’s picture

Status: Needs work » Needs review
FileSize
890 bytes
7.49 KB

Yes, it takes a bit to see what is happening here. I agree that more documentation is a good idea. However, I placed at the top of the doc block instead of in the example sp it is more visible.

alexpott’s picture

Thanks @quietone that looks great. Should this issue be a bug fix?

quietone’s picture

Category: Task » Bug report

Yes, I think so.

benjifisher’s picture

Status: Needs review » Needs work
+++ b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
@@ -16,7 +16,8 @@
  * Available configuration keys:
  *   - process: the plugin(s) that will process each element of the source.
  *   - key: runs the process pipeline for the key to determine a new dynamic
- *     name.
+ *     name. If the key is transformed to a NULL then the result of the
+ *     sub_process pipeline is ignored.

What does "transformed to" mean? Does this mean that if the "new dynamic name" is NULL, then the row (array) is skipped?

I think I prefer the suggestion in #25 to say something in the examples section instead of in the configuration section. Maybe both?

quietone’s picture

When the key is NULL, that row in the sub_process is silently ignored. I am cautious on using 'skip' as that could imply that a MigrateSkipProcessException or MigrateSkipRowException is throw, neither of which is true in this case.

benjifisher’s picture

Status: Needs review » Reviewed & tested by the community

Thanks, I think that is clearer.

benjifisher’s picture

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

Should this issue be a bug fix?

On one hand, it looks like a bug to me. In a recent project, I had a D7 site using the media_filter text filter. I found #3061571: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up and thought, "I am glad that I decided to start with Drupal 8.9.0-beta2 instead of 8.8.0". But then I ran the migrations, and I still got errors from that filter, and the format that used it was not migrated.

On the other hand, #3061571: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up is tagged as a Task, not a bug, so maybe this issue should be the same.

Either way, we should be targeting 9.1.x, not 9.0.x, right? I will ask the testbot to check.

alexpott’s picture

Version: 9.1.x-dev » 8.9.x-dev
Status: Reviewed & tested by the community » Fixed

@benjifisher I'd argue that this is a bug exposed by doing #3061571: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up in 8.9.x - hence we should fix all the way back to that release.

Committed and pushed 61c9564477 to 9.1.x and ed21c9b478 to 9.0.x and 83cc4d8e8c to 8.9.x. Thanks!

  • alexpott committed 61c9564 on 9.1.x
    Issue #3126063 by quietone, Wim Leers, DamienMcKenna, benjifisher,...

  • alexpott committed ed21c9b on 9.0.x
    Issue #3126063 by quietone, Wim Leers, DamienMcKenna, benjifisher,...

  • alexpott committed 83cc4d8 on 8.9.x
    Issue #3126063 by quietone, Wim Leers, DamienMcKenna, benjifisher,...
Wim Leers’s picture

🥳

Thanks for the thoughtful discussion and additional refinements here!

And yes, this was indeed very tricky and confusing.

Status: Fixed » Closed (fixed)

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

joseph.olstad’s picture

This fix (patch 30) also works well for D8.8.8 and we don't use migrate so I am willing to hold my nose on that error, we'll be using the patch until our distribution provider gets around to upgrading core to 8.9.x or 9.x

Christopher Riley’s picture

Should this work with 9.2.0 as I am getting the dreaded:

In DiscoveryTrait.php line 53:

[Drupal\Component\Plugin\Exception\PluginNotFoundException]
The "" plugin does not exist. Valid plugin IDs for Drupal\migrate\Plugin\MigratePluginManager are: blah blah blah

jmester13’s picture

I am having this issue on 8.9.16 currently. I haven't migrated from D7, I've been on 8.9.x since the site was created.

Drupal\Component\Plugin\Exception\PluginNotFoundException: The "entity:profile:customer" plugin does not exist. Valid plugin IDs for Drupal\Core\TypedData\TypedDataManager are: ....

(line 53 of /core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php).