updated contrib modules : migrate_tools, migrate_plus, piwik -> matomo, color_field, config_filter, admin_toolbar

This commit is contained in:
2018-09-12 14:41:53 +02:00
parent b01aa458f8
commit 4caa864a8f
176 changed files with 8874 additions and 1500 deletions
@@ -6,7 +6,6 @@ migrate_plugin:
plugin:
type: string
label: 'Plugin'
migrate_destination:
type: migrate_plugin
label: 'Destination'
@@ -25,6 +24,35 @@ migrate_source:
constants:
type: ignore
label: 'Constants'
ids:
type: ignore
label: 'Source IDs schema definition for migrate mapping table'
urls:
type: sequence
label: 'URLs from which to fetch'
sequence:
type: string
data_fetcher_plugin:
type: string
label: 'Fetcher plugin'
data_parser_plugin:
type: string
label: 'Parser plugin'
fields:
type: ignore
label: Mapping of field names to selectors
function:
type: string
label: 'Function to call on the service'
parameters:
type: ignore
label: 'Parameters to pass to function on the service'
response_type:
type: string
label: 'Type of response; XML string, object or array'
item_selector:
type: string
label: 'XPath selector'
migrate_process:
type: migrate_plugin
@@ -71,9 +71,9 @@ migrate_plus.process.get:
type: string
label: 'Source key'
migrate_plus.process.iterator:
migrate_plus.process.sub_process:
type: migrate_process
label: 'Iterator process'
label: 'Sub process'
mapping:
process:
type: ignore
@@ -2,6 +2,8 @@
id: beer_node
label: Beers of the world
migration_group: beer
migration_tags:
- example
source:
plugin: beer_node
destination:
@@ -12,6 +12,10 @@ label: Migrate style categories from the source database to taxonomy terms
# configuration to be merged with our own configuration here).
migration_group: beer
# The category or tag for the migration.
migration_tags:
- example
# Every migration must have a source plugin, which controls the delivery of our
# source data. In this case, our source plugin has the name "beer_term", which
# Drupal resolves to the PHP class defined in
@@ -4,6 +4,8 @@
id: beer_user
label: Beer Drinkers of the world
migration_group: beer
migration_tags:
- example
source:
plugin: beer_user
destination:
@@ -10,8 +10,8 @@ dependencies:
- drupal:menu_ui
- drupal:path
# Information added by Drupal.org packaging script on 2018-02-23
version: '8.x-4.0-beta3'
# Information added by Drupal.org packaging script on 2018-09-06
version: '8.x-4.0'
core: '8.x'
project: 'migrate_plus'
datestamp: 1519400598
datestamp: 1536264189
@@ -11,8 +11,8 @@ dependencies:
- drupal:options
- drupal:taxonomy
# Information added by Drupal.org packaging script on 2018-02-23
version: '8.x-4.0-beta3'
# Information added by Drupal.org packaging script on 2018-09-06
version: '8.x-4.0'
core: '8.x'
project: 'migrate_plus'
datestamp: 1519400598
datestamp: 1536264189
@@ -2,13 +2,16 @@
id: weather_soap
label: SOAP service providing weather.
migration_group: wine
migration_tags:
- advanced example
source:
# We use the SOAP parser source plugin.
plugin: url
data_fetcher_plugin: http # Ignored - SoapClient does the fetching.
data_parser_plugin: soap
# URL of a WSDL endpoint.
urls: http://www.webservicex.net/globalweather.asmx?WSDL
urls:
- http://www.webservicex.net/globalweather.asmx?WSDL
# The function to call on the service, and the parameters to pass. See
# http://www.webservicex.net/New/Home/ServiceDetail/56 for the XML structure
# of this feed - how CountryName is passed within the GetCitiesByCountry
@@ -48,3 +51,6 @@ process:
name: City
destination:
plugin: entity:taxonomy_term
migration_dependencies:
required: {}
optional: {}
@@ -2,6 +2,8 @@
id: wine_role_json
label: JSON feed of roles (positions)
migration_group: wine
migration_tags:
- advanced example
source:
# We use the JSON source plugin.
plugin: url
@@ -17,7 +19,8 @@ source:
# Normally, this is one or more fully-qualified URLs or file paths. Because
# we can't hardcode your local URL, we provide a relative path here which
# hook_install() will rewrite to a full URL for the current site.
urls: /migrate_example_advanced_position?_format=json
urls:
- /migrate_example_advanced_position?_format=json
# An xpath-like selector corresponding to the items to be imported.
item_selector: position
# Under 'fields', we list the data items to be imported. The first level keys
@@ -2,6 +2,8 @@
id: wine_role_xml
label: XML feed of roles (positions)
migration_group: wine
migration_tags:
- advanced example
source:
# We use the XML data parser plugin.
plugin: url
@@ -10,7 +12,8 @@ source:
# Normally, this is one or more fully-qualified URLs or file paths. Because
# we can't hardcode your local URL, we provide a relative path here which
# hook_install() will rewrite to a full URL for the current site.
urls: /migrate_example_advanced_position?_format=xml
urls:
- /migrate_example_advanced_position?_format=xml
# Visit the URL above (relative to your site root) and look at it. You can see
# that <response> is the outer element, and each item we want to import is a
# <position> element. The item_xpath value is the xpath to use to query the
@@ -1,6 +1,8 @@
id: wine_terms
label: Migrate all categories into Drupal taxonomy terms
migration_group: wine
migration_tags:
- advanced example
source:
plugin: wine_term
destination:
@@ -3,13 +3,16 @@
id: wine_variety_list
label: XML feed of varieties
migration_group: wine
migration_tags:
- advanced example
source:
# We use the XML source plugin.
plugin: xml
# Normally, this is one or more fully-qualified URLs or file paths. Because
# we can't hardcode your local URL, we provide a relative path here which
# hook_install() will rewrite to a full URL for the current site.
urls: /migrate_example_advanced_variety_list?_format=xml
urls:
- /migrate_example_advanced_variety_list?_format=xml
item_url: /migrate_example_advanced_variety_list/:id?_format=xml
id_selector: /response/items
# Visit the URL above (relative to your site root) and look at it. You can see
@@ -2,6 +2,8 @@
id: wine_variety_multi_xml
label: XML feed of varieties
migration_group: wine
migration_tags:
- advanced example
source:
# We use the XML source plugin.
plugin: url
@@ -8,8 +8,8 @@ dependencies:
- migrate_plus:migrate_example_advanced_setup
- migrate_plus:migrate_plus
# Information added by Drupal.org packaging script on 2018-02-23
version: '8.x-4.0-beta3'
# Information added by Drupal.org packaging script on 2018-09-06
version: '8.x-4.0'
core: '8.x'
project: 'migrate_plus'
datestamp: 1519400598
datestamp: 1536264189
@@ -21,7 +21,11 @@ function migrate_example_advanced_install() {
if ($wine_role_xml_migration) {
$source = $wine_role_xml_migration->get('source');
$request = \Drupal::request();
$source['urls'] = 'http://' . $request->getHttpHost() . $source['urls'];
$urls = [];
foreach ($source['urls'] as $url) {
$urls[] = 'http://' . $request->getHttpHost() . $url;
}
$source['urls'] = $urls;
$wine_role_xml_migration->set('source', $source);
$wine_role_xml_migration->save();
}
@@ -30,7 +34,11 @@ function migrate_example_advanced_install() {
if ($wine_role_json_migration) {
$source = $wine_role_json_migration->get('source');
$request = \Drupal::request();
$source['urls'] = 'http://' . $request->getHttpHost() . $source['urls'];
$urls = [];
foreach ($source['urls'] as $url) {
$urls[] = 'http://' . $request->getHttpHost() . $url;
}
$source['urls'] = $urls;
$wine_role_json_migration->set('source', $source);
$wine_role_json_migration->save();
}
@@ -11,8 +11,8 @@ dependencies:
- drupal:taxonomy
- drupal:rest
# Information added by Drupal.org packaging script on 2018-02-23
version: '8.x-4.0-beta3'
# Information added by Drupal.org packaging script on 2018-09-06
version: '8.x-4.0'
core: '8.x'
project: 'migrate_plus'
datestamp: 1519400598
datestamp: 1536264189
@@ -6,8 +6,8 @@ package: Migration
dependencies:
- drupal:migrate (>=8.3)
# Information added by Drupal.org packaging script on 2018-02-23
version: '8.x-4.0-beta3'
# Information added by Drupal.org packaging script on 2018-09-06
version: '8.x-4.0'
core: '8.x'
project: 'migrate_plus'
datestamp: 1519400598
datestamp: 1536264189
@@ -2,8 +2,13 @@
namespace Drupal\migrate_plus\Plugin\migrate\process;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Plugin\MigratePluginManager;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* This plugin generates entities within the process plugin.
@@ -16,10 +21,10 @@ use Drupal\migrate\Row;
*
* All the configuration from the lookup plugin applies here. In its most
* simple form, this plugin needs no configuration. If there are fields on the
* generated entity that are required or need some default value, that can be
* provided via a default_values configuration option.
* generated entity that are required or need some value, their values can be
* provided via values and/or default_values configuration options.
*
* Example usage with default_values configuration:
* Example usage with values and default_values configuration:
* @code
* destination:
* plugin: 'entity:node'
@@ -27,20 +32,86 @@ use Drupal\migrate\Row;
* type:
* plugin: default_value
* default_value: page
* foo: bar
* field_tags:
* plugin: entity_generate
* source: tags
* default_values:
* description: Default description
* field_long_description: Default long description
* values:
* field_long_description: some_source_field
* field_foo: '@foo'
* @endcode
*/
class EntityGenerate extends EntityLookup {
/**
* The row from the source to process.
*
* @var \Drupal\migrate\Row
*/
protected $row;
/**
* The MigrateExecutable instance.
*
* @var \Drupal\migrate\MigrateExecutable
*/
protected $migrateExecutable;
/**
* The get process plugin instance.
*
* @var \Drupal\migrate\Plugin\migrate\process\Get
*/
protected $getProcessPlugin;
/**
* EntityGenerate constructor.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $pluginId
* The plugin_id for the plugin instance.
* @param mixed $pluginDefinition
* The plugin implementation definition.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The migration.
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
* The $entityManager instance.
* @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selectionPluginManager
* The $selectionPluginManager instance.
* @param \Drupal\migrate\Plugin\MigratePluginManager $migratePluginManager
* The MigratePluginManager instance.
*/
public function __construct(array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration, EntityManagerInterface $entityManager, SelectionPluginManagerInterface $selectionPluginManager, MigratePluginManager $migratePluginManager) {
parent::__construct($configuration, $pluginId, $pluginDefinition, $migration, $entityManager, $selectionPluginManager);
if (isset($configuration['values'])) {
$this->getProcessPlugin = $migratePluginManager->createInstance('get', ['source' => $configuration['values']]);
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition, MigrationInterface $migration = NULL) {
return new static(
$configuration,
$pluginId,
$pluginDefinition,
$migration,
$container->get('entity.manager'),
$container->get('plugin.manager.entity_reference_selection'),
$container->get('plugin.manager.migrate.process')
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
$this->row = $row;
$this->migrateExecutable = $migrateExecutable;
// Creates an entity if the lookup determines it doesn't exist.
if (!($result = parent::transform($value, $migrateExecutable, $row, $destinationProperty))) {
$result = $this->generateEntity($value);
@@ -94,6 +165,13 @@ class EntityGenerate extends EntityLookup {
$entity_values[$key] = $value;
}
}
// Gather any additional properties/fields.
if (isset($this->configuration['values']) && is_array($this->configuration['values'])) {
foreach ($this->configuration['values'] as $key => $property) {
$source_value = $this->getProcessPlugin->transform(NULL, $this->migrateExecutable, $this->row, $property);
$entity_values[$key] = $source_value;
}
}
return $entity_values;
}
@@ -2,6 +2,7 @@
namespace Drupal\migrate_plus\Plugin\migrate\process;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -152,6 +153,11 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrateExecutable, Row $row, $destinationProperty) {
// If the source data is an empty array, return the same.
if (gettype($value) === 'array' && count($value) === 0) {
return [];
}
// In case of subfields ('field_reference/target_id'), extract the field
// name only.
$parts = explode('/', $destinationProperty);
@@ -270,7 +276,8 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn
if (!$ignoreCase) {
// Returns the entity's identifier.
foreach ($results as $k => $identifier) {
$result_value = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier)->{$this->lookupValueKey}->value;
$entity = $this->entityManager->getStorage($this->lookupEntityType)->load($identifier);
$result_value = $entity instanceof ConfigEntityInterface ? $entity->get($this->lookupValueKey) : $entity->get($this->lookupValueKey)->value;
if (($multiple && !in_array($result_value, $value, TRUE)) || (!$multiple && $result_value !== $value)) {
unset($results[$k]);
}
@@ -41,8 +41,8 @@ use Drupal\migrate\Row;
* paragraphs_field:
* plugin: merge
* source:
* - @temp_body
* - @temp_images
* - '@temp_body'
* - '@temp_images'
* destination:
* plugin: 'entity:node'
*/
@@ -53,12 +53,12 @@ class Merge extends ProcessPluginBase {
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
if (!is_array($value)) {
throw new MigrateException('Input should be an array.');
throw new MigrateException(sprintf('Merge process failed for destination property (%s): input is not an array.', $destination_property));
}
$new_value = [];
foreach ($value as $item) {
foreach($value as $i => $item) {
if (!is_array($item)) {
throw new MigrateException('One of the items is not an array that can be merged.');
throw new MigrateException(sprintf('Merge process failed for destination property (%s): index (%s) in the source value is not an array that can be merged.', $destination_property, $i));
}
$new_value = array_merge($new_value, $item);
}
@@ -0,0 +1,58 @@
<?php
namespace Drupal\migrate_plus\Plugin\migrate\process;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Treat an array of values as a separate / individual values.
*
* @code
* process:
* field_authors:
* -
* plugin: explode
* delimiter: ', '
* source: authors
* -
* plugin: single_value
* -
* plugin: callback
* callable: custom_sort_authors
* -
* plugin: multiple_values
* @endcode
*
* Assume the "authors" field contains comma separated author names.
*
* We split the names into multiple values and then use the "single_value"
* plugin to treat them as a single array of author names. After that, we
* pass the values through a custom sort. Callback multiple setting is false. To
* convert from a single value to multiple, use the "multiple_values" plugin. It
* will make the next plugin treat the values individually instead of an array
* of values.
*
* @MigrateProcessPlugin(
* id = "multiple_values",
* handle_multiples = TRUE
* )
*/
class MultipleValues extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
return $value;
}
/**
* {@inheritdoc}
*/
public function multiple() {
return TRUE;
}
}
@@ -0,0 +1,45 @@
<?php
namespace Drupal\migrate_plus\Plugin\migrate\process;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Treat an array of values as a single value.
*
* @code
* process:
* field_authors:
* -
* plugin: explode
* delimiter: ', '
* source: authors
* -
* plugin: single_value
* @endcode
*
* Assume the "authors" field contains comma separated author names.
*
* After the explode, we end up with each author name as an individual value.
* But if we want to perform a sort on all values using a callback, we will
* need to send all the values to a callable together as an array of author
* names. Calling the "single_value" plugin in such a case will combine all the
* values into a single array for the next plugin.
*
* @MigrateProcessPlugin(
* id = "single_value",
* handle_multiples = TRUE
* )
*/
class SingleValue extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
return $value;
}
}
@@ -15,6 +15,44 @@ use Drupal\migrate\Row;
* @MigrateProcessPlugin(
* id = "skip_on_value"
* )
*
* Available configuration keys:
* - value: An single value or array of values against which the source value
* should be compared.
* - not_equals: (optional) If set, skipping occurs when values are not equal.
* - method: What to do if the input value is empty. Possible values:
* - row: Skips the entire row when an empty value is encountered.
* - process: Prevents further processing of the input property when the value
* is empty.
*
* Examples:
*
* Example usage with minimal configuration:
* @code
* type:
* plugin: skip_on_value
* source: content_type
* method: row
* value: blog
* @endcode
*
* The above example will skip processing the input property if the content_type
* source field equals "blog".
*
* Example usage with full configuration:
* @code
* type:
* plugin: skip_on_value
* not_equals: true
* source: content_type
* method: row
* value:
* - article
* - testimonial
* @endcode
*
* The above example will skip processing any row for which the source row's
* content type field is not "article" or "testimonial".
*/
class SkipOnValue extends ProcessPluginBase {
@@ -27,10 +65,15 @@ class SkipOnValue extends ProcessPluginBase {
}
if (is_array($this->configuration['value'])) {
$value_in_array = FALSE;
$not_equals = isset($this->configuration['not_equals']);
foreach ($this->configuration['value'] as $skipValue) {
if ($this->compareValue($value, $skipValue, !isset($this->configuration['not_equals']))) {
throw new MigrateSkipRowException();
}
$value_in_array |= $this->compareValue($value, $skipValue);
}
if (($not_equals && !$value_in_array) || (!$not_equals && $value_in_array)) {
throw new MigrateSkipRowException();
}
}
elseif ($this->compareValue($value, $this->configuration['value'], !isset($this->configuration['not_equals']))) {
@@ -49,10 +92,15 @@ class SkipOnValue extends ProcessPluginBase {
}
if (is_array($this->configuration['value'])) {
$value_in_array = FALSE;
$not_equals = isset($this->configuration['not_equals']);
foreach ($this->configuration['value'] as $skipValue) {
if ($this->compareValue($value, $skipValue, !isset($this->configuration['not_equals']))) {
throw new MigrateSkipProcessException();
}
$value_in_array |= $this->compareValue($value, $skipValue);
}
if (($not_equals && !$value_in_array) || (!$not_equals && $value_in_array)) {
throw new MigrateSkipProcessException();
}
}
elseif ($this->compareValue($value, $this->configuration['value'], !isset($this->configuration['not_equals']))) {
@@ -15,13 +15,6 @@ use Drupal\migrate_plus\DataParserPluginBase;
*/
class Json extends DataParserPluginBase implements ContainerFactoryPluginInterface {
/**
* The request headers passed to the data fetcher.
*
* @var array
*/
protected $headers = [];
/**
* Iterator over the JSON data.
*
@@ -119,7 +112,12 @@ class Json extends DataParserPluginBase implements ContainerFactoryPluginInterfa
$field_data = $current;
$field_selectors = explode('/', trim($selector, '/'));
foreach ($field_selectors as $field_selector) {
$field_data = $field_data[$field_selector];
if (is_array($field_data) && array_key_exists($field_selector, $field_data)) {
$field_data = $field_data[$field_selector];
}
else {
$field_data = '';
}
}
$this->currentItem[$field_name] = $field_data;
}
@@ -77,7 +77,7 @@ class SimpleXml extends DataParserPluginBase {
}
// Reduce single-value results to scalars.
foreach ($this->currentItem as $field_name => $values) {
if (count($values) == 1) {
if (is_array($values) && count($values) == 1) {
$this->currentItem[$field_name] = reset($values);
}
}
@@ -0,0 +1,21 @@
[
{
"id": "1",
"title": "Title",
"video": {
"title": "Video title",
"url": "https://localhost/"
}
},
{
"id": "2",
"video": {
"title": "Video title",
"url": "https://localhost/"
}
},
{
"id": "3",
"title": "Title 3"
}
]
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<items>
<item id="1">
<values title="Values">
<value>Value 1</value>
<value>Value 2</value>
</values>
</item>
<item id="2">
<values title="Values">
<value>Value 1 (single)</value>
</values>
</item>
</items>
@@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\migrate_plus\Functional;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
/**
* Simple test to ensure that main page loads with module enabled.
*
* @group migrate_plus
*/
class LoadTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'migrate_plus',
'migrate_example',
'migrate_example_advanced',
];
/**
* A user with permission to administer site configuration.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->user = $this->drupalCreateUser(['administer site configuration']);
$this->drupalLogin($this->user);
}
/**
* Tests that the home page loads with a 200 response.
*/
public function testLoad() {
$this->drupalGet(Url::fromRoute('<front>'));
$this->assertSession()->statusCodeEquals(200);
}
}
@@ -2,6 +2,7 @@
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate\process;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
@@ -119,12 +120,16 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
public function testTransform(array $definition, array $expected, array $preSeed = []) {
// Pre seed some test data.
foreach ($preSeed as $storageName => $values) {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->container
->get('entity_type.manager')
->getStorage($storageName);
$entity = $storage->create($values);
$entity->save();
// If the first element of $values is a non-empty array, create multiple
// entities. Otherwise, create just one entity.
if (isset($values[0])) {
foreach ($values as $itemValues) {
$this->createTestData($storageName, $itemValues);
}
}
else {
$this->createTestData($storageName, $values);
}
}
/** @var \Drupal\migrate\Plugin\Migration $migration */
@@ -139,15 +144,54 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
$properties = array_diff_key($row, array_flip(['id']));
foreach ($properties as $property => $value) {
if (is_array($value)) {
foreach ($value as $key => $expectedValue) {
if (empty($expectedValue)) {
$this->assertEmpty($entity->{$property}->getValue(), "Expected value is empty but field $property is not empty.");
}
elseif ($entity->{$property}->getValue()) {
$this->assertEquals($expectedValue, $entity->{$property}[0]->entity->$key->value);
if (empty($value)) {
$this->assertEmpty($entity->{$property}->getValue(), "Expected value is 'unset' but field $property is set.");
}
else {
// Check if we're testing multiple values in one field. If so, loop
// through them one-by-one and check that they're present in the
// $entity.
if (isset($value[0])) {
foreach ($value as $valueID => $valueToCheck) {
foreach ($valueToCheck as $key => $expectedValue) {
if (empty($expectedValue)) {
if (!$entity->{$property}->isEmpty()) {
$this->assertTrue($entity->{$property}[0]->entity->$key->isEmpty(), "Expected value is empty but field $property.$key is not empty.");
}
else {
$this->assertTrue($entity->{$property}->isEmpty(), "FOOBAR Expected value is empty but field $property is not empty.");
}
}
elseif ($entity->{$property}->getValue()) {
$this->assertEquals($expectedValue, $entity->{$property}[$valueID]->entity->$key->value);
}
else {
$this->fail("Expected value: $expectedValue does not exist in $property.");
}
}
}
}
// If we get to this point, we're only checking a
// single field value.
else {
$this->fail("Expected value: $expectedValue does not exist in $property.");
foreach ($value as $key => $expectedValue) {
if (empty($expectedValue)) {
if (!$entity->{$property}->isEmpty()) {
$this->assertTrue($entity->{$property}[0]->entity->$key->isEmpty(), "Expected value is empty but field $property.$key is not empty.");
}
else {
$this->assertTrue($entity->{$property}->isEmpty(), "BINBAZ Expected value is empty but field $property is not empty.");
}
}
elseif ($entity->{$property}->getValue()) {
$referenced_entity = $entity->{$property}[0]->entity;
$result_value = $referenced_entity instanceof ConfigEntityInterface ? $referenced_entity->get($key) : $referenced_entity->get($key)->value;
$this->assertEquals($expectedValue, $result_value);
}
else {
$this->fail("Expected value: $expectedValue does not exist in $property.");
}
}
}
}
}
@@ -314,6 +358,406 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
],
],
],
'provide values' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'title' => 'content item 1',
'term' => 'Apples',
],
[
'id' => 2,
'title' => 'content item 2',
'term' => 'Bananas',
],
[
'id' => 3,
'title' => 'content item 3',
'term' => 'Grapes',
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'type' => [
'plugin' => 'default_value',
'default_value' => $this->bundle,
],
'title' => 'title',
'term_upper' => [
'plugin' => 'callback',
'source' => 'term',
'callable' => 'strtoupper',
],
$this->fieldName => [
'plugin' => 'entity_generate',
'source' => 'term',
'values' => [
'description' => '@term_upper',
],
],
],
'destination' => [
'plugin' => 'entity:node',
],
],
'expected' => [
'row 1' => [
'id' => 1,
'title' => 'content item 1',
$this->fieldName => [
'tid' => 2,
'name' => 'Apples',
'description' => 'APPLES',
],
],
'row 2' => [
'id' => 2,
'title' => 'content item 2',
$this->fieldName => [
'tid' => 3,
'name' => 'Bananas',
'description' => 'BANANAS',
],
],
'row 3' => [
'id' => 3,
'title' => 'content item 3',
$this->fieldName => [
'tid' => 1,
'name' => 'Grapes',
'description' => NULL,
],
],
],
'pre seed' => [
'taxonomy_term' => [
'name' => 'Grapes',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
],
],
'lookup single existing term returns correct term' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'title' => 'content item 1',
'term' => 'Grapes',
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'type' => [
'plugin' => 'default_value',
'default_value' => $this->bundle,
],
'title' => 'title',
$this->fieldName => [
'plugin' => 'entity_lookup',
'source' => 'term',
],
],
'destination' => [
'plugin' => 'entity:node',
],
],
'expected' => [
'row 1' => [
'id' => 1,
'title' => 'content item 1',
$this->fieldName => [
'tid' => 1,
'name' => 'Grapes',
],
],
],
'pre seed' => [
'taxonomy_term' => [
'name' => 'Grapes',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
],
],
'lookup single missing term returns null value' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'title' => 'content item 1',
'term' => 'Apple',
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'type' => [
'plugin' => 'default_value',
'default_value' => $this->bundle,
],
'title' => 'title',
$this->fieldName => [
'plugin' => 'entity_lookup',
'source' => 'term',
],
],
'destination' => [
'plugin' => 'entity:node',
],
],
'expected' => [
'row 1' => [
'id' => 1,
'title' => 'content item 1',
$this->fieldName => [],
],
],
'pre seed' => [
'taxonomy_term' => [
'name' => 'Grapes',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
],
],
'lookup multiple existing terms returns correct terms' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'title' => 'content item 1',
'term' => [
'Grapes',
'Apples',
],
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'title' => 'title',
'type' => [
'plugin' => 'default_value',
'default_value' => $this->bundle,
],
$this->fieldName => [
'plugin' => 'entity_lookup',
'source' => 'term',
],
],
'destination' => [
'plugin' => 'entity:node',
],
],
'expected' => [
'row 1' => [
'id' => 1,
'title' => 'content item 1',
$this->fieldName => [
[
'tid' => 1,
'name' => 'Grapes',
],
[
'tid' => 2,
'name' => 'Apples',
],
],
],
],
'pre seed' => [
'taxonomy_term' => [
[
'name' => 'Grapes',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
[
'name' => 'Apples',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
],
],
],
'lookup multiple mixed terms returns correct terms' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'title' => 'content item 1',
'term' => [
'Grapes',
'Pears',
],
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'title' => 'title',
'type' => [
'plugin' => 'default_value',
'default_value' => $this->bundle,
],
$this->fieldName => [
'plugin' => 'entity_lookup',
'source' => 'term',
],
],
'destination' => [
'plugin' => 'entity:node',
],
],
'expected' => [
'row 1' => [
'id' => '1',
'title' => 'content item 1',
$this->fieldName => [
[
'tid' => 1,
'name' => 'Grapes',
],
],
],
],
'pre seed' => [
'taxonomy_term' => [
[
'name' => 'Grapes',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
[
'name' => 'Apples',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
],
],
],
'lookup with empty term value returns no terms' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'title' => 'content item 1',
'term' => [],
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'title' => 'title',
'type' => [
'plugin' => 'default_value',
'default_value' => $this->bundle,
],
$this->fieldName => [
'plugin' => 'entity_lookup',
'source' => 'term',
],
],
'destination' => [
'plugin' => 'entity:node',
],
],
'expected' => [
'row 1' => [
'id' => 1,
'title' => 'content item 1',
$this->fieldName => [],
],
],
'pre seed' => [
'taxonomy_term' => [
'name' => 'Grapes',
'vid' => $this->vocabulary,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
],
],
],
'lookup config entity' => [
'definition' => [
'source' => [
'plugin' => 'embedded_data',
'data_rows' => [
[
'id' => 1,
'name' => 'user 1',
'mail' => 'user1@user1.com',
'roles' => ['role_1'],
],
],
'ids' => [
'id' => ['type' => 'integer'],
],
],
'process' => [
'id' => 'id',
'name' => 'name',
'roles' => [
'plugin' => 'entity_lookup',
'entity_type' => 'user_role',
'value_key' => 'id',
'source' => 'roles',
],
],
'destination' => [
'plugin' => 'entity:user',
],
],
'expected' => [
'row 1' => [
'id' => 1,
'name' => 'user 1',
'roles' => [
'id' => 'role_1',
'label' => 'Role 1',
],
],
],
'pre seed' => [
'user_role' => [
'id' => 'role_1',
'label' => 'Role 1',
],
],
],
];
}
@@ -324,4 +768,21 @@ class EntityGenerateTest extends KernelTestBase implements MigrateMessageInterfa
$this->assertTrue($type == 'status', $message);
}
/**
* Create pre-seed test data.
*
* @param string $storageName
* The storage manager to create.
* @param array $values
* The values to use when creating the entity.
*/
private function createTestData($storageName, array $values) {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->container
->get('entity_type.manager')
->getStorage($storageName);
$entity = $storage->create($values);
$entity->save();
}
}
@@ -0,0 +1,112 @@
<?php
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate_plus\data_parser;
use Drupal\KernelTests\KernelTestBase;
/**
* Test of the data_parser Json migrate_plus plugin.
*
* @group migrate_plus
*/
class JsonTest extends KernelTestBase {
public static $modules = ['migrate', 'migrate_plus'];
/**
* Tests missing properties in json file.
*
* @param string $file
* File name in tests/data/ directory of this module.
* @param array $ids
* Array of ids to pass to the plugin.
* @param array $fields
* Array of fields to pass to the plugin.
* @param array $expected
* Expected array from json decoded file.
*
* @dataProvider jsonBaseDataProvider
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
* @throws \Exception
*/
public function testMissingProperties($file, array $ids, array $fields, array $expected) {
$path = $this->container
->get('module_handler')
->getModule('migrate_plus')
->getPath();
$url = $path . '/tests/data/' . $file;
/** @var \Drupal\migrate_plus\DataParserPluginManager $plugin_manager */
$plugin_manager = $this->container
->get('plugin.manager.migrate_plus.data_parser');
$conf = [
'plugin' => 'url',
'data_fetcher_plugin' => 'file',
'data_parser_plugin' => 'json',
'destination' => 'node',
'urls' => [$url],
'ids' => $ids,
'fields' => $fields,
'item_selector' => NULL,
];
$json_parser = $plugin_manager->createInstance('json', $conf);
$data = [];
foreach ($json_parser as $item) {
$data[] = $item;
}
$this->assertEquals($expected, $data);
}
/**
* Provides multiple test cases for the testMissingProperty method.
*
* @return array
* The test cases.
*/
public function jsonBaseDataProvider() {
return [
'missing properties' => [
'file' => 'missing_properties.json',
'ids' => ['id' => ['type' => 'integer']],
'fields' => [
[
'name' => 'id',
'label' => 'Id',
'selector' => '/id',
],
[
'name' => 'title',
'label' => 'Title',
'selector' => '/title',
],
[
'name' => 'video_url',
'label' => 'Video url',
'selector' => '/video/url',
],
],
'expected' => [
[
'id' => '1',
'title' => 'Title',
'video_url' => 'https://localhost/',
],
[
'id' => '2',
'title' => '',
'video_url' => 'https://localhost/',
],
[
'id' => '3',
'title' => 'Title 3',
'video_url' => '',
],
],
],
];
}
}
@@ -0,0 +1,77 @@
<?php
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate_plus\data_parser;
use Drupal\KernelTests\KernelTestBase;
/**
* Test of the data_parser SimpleXml migrate_plus plugin.
*
* @group migrate_plus
*/
class SimpleXmlTest extends KernelTestBase {
public static $modules = ['migrate', 'migrate_plus'];
/**
* Tests reducing single values.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
* @throws \Exception
*/
public function testReduceSingleValue() {
$path = $this->container
->get('module_handler')
->getModule('migrate_plus')
->getPath();
$url = $path . '/tests/data/simple_xml_reduce_single_value.xml';
/** @var \Drupal\migrate_plus\DataParserPluginManager $plugin_manager */
$plugin_manager = $this->container
->get('plugin.manager.migrate_plus.data_parser');
$conf = [
'plugin' => 'url',
'data_fetcher_plugin' => 'file',
'data_parser_plugin' => 'simple_xml',
'destination' => 'node',
'urls' => [$url],
'ids' => ['id' => ['type' => 'integer']],
'fields' => [
[
'name' => 'id',
'label' => 'Id',
'selector' => '@id',
],
[
'name' => 'values',
'label' => 'Values',
'selector' => 'values',
],
],
'item_selector' => '/items/item',
];
$parser = $plugin_manager->createInstance('simple_xml', $conf);
$data = [];
foreach ($parser as $item) {
$values = [];
foreach ($item['values'] as $value) {
$values[] = (string) $value;
}
$data[] = $values;
}
$expected = [
[
'Value 1',
'Value 2',
],
[
'Value 1 (single)',
],
];
$this->assertEquals($expected, $data);
}
}
@@ -0,0 +1,32 @@
<?php
namespace Drupal\Tests\migrate_plus\Unit\process;
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
use Drupal\migrate_plus\Plugin\migrate\process\MultipleValues;
/**
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate\process\MultipleValues
* @group migrate
*/
class MultipleValuesTest extends MigrateProcessTestCase {
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->plugin = new MultipleValues([], 'multiple_values', []);
parent::setUp();
}
/**
* Test input treated as multiple value output.
*/
public function testTreatAsMultiple() {
$value = ['v1', 'v2', 'v3'];
$output = $this->plugin->transform($value, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame($output, $value);
$this->assertTrue($this->plugin->multiple());
}
}
@@ -0,0 +1,32 @@
<?php
namespace Drupal\Tests\migrate_plus\Unit\process;
use Drupal\Tests\migrate\Unit\process\MigrateProcessTestCase;
use Drupal\migrate_plus\Plugin\migrate\process\SingleValue;
/**
* @coversDefaultClass \Drupal\migrate_plus\Plugin\migrate\process\SingleValue
* @group migrate
*/
class SingleValueTest extends MigrateProcessTestCase {
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->plugin = new SingleValue([], 'single_value', []);
parent::setUp();
}
/**
* Test input treated as single value output.
*/
public function testTreatAsSingle() {
$value = ['v1', 'v2', 'v3'];
$output = $this->plugin->transform($value, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame($output, $value);
$this->assertFalse($this->plugin->multiple());
}
}
@@ -65,6 +65,36 @@ class SkipOnValueTest extends MigrateProcessTestCase {
$this->assertEquals($value, '4');
}
/**
* @covers ::process
*/
public function testProcessBypassesOnMultipleNonValue() {
$configuration['method'] = 'process';
$configuration['value'] = [1, 1, 2, 3, 5, 8];
$configuration['not_equals'] = TRUE;
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
->transform(5, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertEquals($value, '5');
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
->transform(1, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertEquals($value, '1');
}
/**
* @covers ::row
*/
public function testRowBypassesOnMultipleNonValue() {
$configuration['method'] = 'row';
$configuration['value'] = [1, 1, 2, 3, 5, 8];
$configuration['not_equals'] = TRUE;
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
->transform(5, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertEquals($value, '5');
$value = (new SkipOnValue($configuration, 'skip_on_value', []))
->transform(1, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertEquals($value, '1');
}
/**
* @covers ::row
*/