From 4ec07d680e55459e8ae86b82a18bc68c10e930c3 Mon Sep 17 00:00:00 2001
From: Dave Long <dave@longwaveconsulting.com>
Date: Sat, 17 Feb 2024 15:45:25 +0000
Subject: [PATCH] Issue #3365895 by danflanagan8, benjifisher, mikelutz: When
 sub_process encounters a row skip, it should skip its internal row, and not
 bubble up to the outer row

---
 .../src/Plugin/migrate/process/SubProcess.php |   8 +-
 .../Kernel/process/SubProcessWithSkipTest.php | 144 ++++++++++++++++++
 2 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 core/modules/migrate/tests/src/Kernel/process/SubProcessWithSkipTest.php

diff --git a/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
index 2017017f2d61..29115b66e306 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/SubProcess.php
@@ -3,6 +3,7 @@
 namespace Drupal\migrate\Plugin\migrate\process;
 
 use Drupal\migrate\MigrateException;
+use Drupal\migrate\MigrateSkipRowException;
 use Drupal\migrate\ProcessPluginBase;
 use Drupal\migrate\MigrateExecutableInterface;
 use Drupal\migrate\Row;
@@ -208,7 +209,12 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
           throw new MigrateException(sprintf("Input array should hold elements of type array, instead element was of type '%s'", gettype($new_value)));
         }
         $new_row = new Row($new_value + $source);
-        $migrate_executable->processRow($new_row, $this->configuration['process']);
+        try {
+          $migrate_executable->processRow($new_row, $this->configuration['process']);
+        }
+        catch (MigrateSkipRowException $e) {
+          continue;
+        }
         $destination = $new_row->getDestination();
         if (array_key_exists('key', $this->configuration)) {
           $key = $this->transformKey($key, $migrate_executable, $new_row);
diff --git a/core/modules/migrate/tests/src/Kernel/process/SubProcessWithSkipTest.php b/core/modules/migrate/tests/src/Kernel/process/SubProcessWithSkipTest.php
new file mode 100644
index 000000000000..ad89fceeebf3
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/process/SubProcessWithSkipTest.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Drupal\Tests\migrate\Kernel\process;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate\Plugin\MigrationInterface;
+
+/**
+ * Tests process pipelines when a sub_process skips a row or process.
+ *
+ * @group migrate
+ */
+class SubProcessWithSkipTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['migrate'];
+
+  /**
+   * Provides the test migration definition.
+   *
+   * @return array
+   */
+  public function getDefinition() {
+    return [
+      'source' => [
+        'plugin' => 'embedded_data',
+        'data_rows' => [
+          [
+            'id' => 'skip_test',
+            'my_array_of_arrays' => [
+              [
+                'key_1' => 'foo',
+                'key_2' => 'bar',
+              ],
+              [
+                'key_1' => NULL,
+                'key_2' => 'baz',
+              ],
+            ],
+          ],
+        ],
+        'ids' => [
+          'id' => ['type' => 'string'],
+        ],
+      ],
+      'process' => [
+        'first' => [
+          'plugin' => 'default_value',
+          'default_value' => 'something outside of sub_process',
+        ],
+        'second' => [
+          'plugin' => 'sub_process',
+          'source' => 'my_array_of_arrays',
+          'process' => [
+            'prop_1' => [
+              [
+                'plugin' => 'skip_on_empty',
+                'source' => 'key_1',
+              ],
+              // We put a process after skip_on_empty to better test skipping
+              // a process.
+              [
+                'plugin' => 'get',
+                'source' => 'key_2',
+              ],
+            ],
+            'prop_2' => 'key_2',
+          ],
+        ],
+      ],
+      'destination' => [
+        'plugin' => 'config',
+        'config_name' => 'migrate_test.settings',
+      ],
+    ];
+  }
+
+  /**
+   * Test use of skip_on_empty within sub_process.
+   *
+   * @dataProvider providerTestSubProcessSkip
+   *
+   * @param string $method
+   *   The method to use with skip_on_empty (row or process).
+   * @param array $expected_data
+   *   The expected result of the migration.
+   */
+  public function testSubProcessSkip(string $method, array $expected_data): void {
+    $definition = $this->getDefinition();
+    $definition['process']['second']['process']['prop_1'][0]['method'] = $method;
+
+    $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
+
+    $executable = new MigrateExecutable($migration);
+    $result = $executable->import();
+
+    // Migration needs to succeed before further assertions are made.
+    $this->assertSame(MigrationInterface::RESULT_COMPLETED, $result);
+
+    // Compare with expected data.
+    $this->assertEquals($expected_data, \Drupal::config('migrate_test.settings')->get());
+  }
+
+  /**
+   * Data provider for testNotFoundSubProcess().
+   *
+   * @return array
+   */
+  public function providerTestSubProcessSkip(): array {
+    return [
+      'skip row' => [
+        'method' => 'row',
+        'expected' => [
+          'first' => 'something outside of sub_process',
+          'second' => [
+            [
+              'prop_1' => 'bar',
+              'prop_2' => 'bar',
+            ],
+          ],
+        ],
+      ],
+      'skip process' => [
+        'method' => 'process',
+        'expected' => [
+          'first' => 'something outside of sub_process',
+          'second' => [
+            [
+              'prop_1' => 'bar',
+              'prop_2' => 'bar',
+            ],
+            [
+              'prop_2' => 'baz',
+            ],
+          ],
+        ],
+      ],
+    ];
+  }
+
+}
-- 
GitLab