diff --git a/config/schema/helfi_api_base.schema.yml b/config/schema/helfi_api_base.schema.yml new file mode 100644 index 00000000..ad75d6d1 --- /dev/null +++ b/config/schema/helfi_api_base.schema.yml @@ -0,0 +1,3 @@ +action.configuration.remote_entity:migration_update:*: + type: action_configuration_default + label: 'Update given remote entity' diff --git a/src/Entity/RemoteEntityBase.php b/src/Entity/RemoteEntityBase.php index cef78342..4af78e19 100644 --- a/src/Entity/RemoteEntityBase.php +++ b/src/Entity/RemoteEntityBase.php @@ -37,6 +37,16 @@ abstract class RemoteEntityBase extends ContentEntityBase { */ protected bool $resetSyncAttempts = TRUE; + /** + * Get sthe migration name. + * + * @return string|null + * The migration name. + */ + public static function getMigration() : ? string { + return NULL; + } + /** * {@inheritdoc} */ diff --git a/src/Plugin/Action/MigrationUpdateAction.php b/src/Plugin/Action/MigrationUpdateAction.php new file mode 100644 index 00000000..7a676023 --- /dev/null +++ b/src/Plugin/Action/MigrationUpdateAction.php @@ -0,0 +1,121 @@ +migrationPluginManager = $container->get('plugin.manager.migration'); + $instance->entityTypeManager = $container->get('entity_type.manager'); + + return $instance; + } + + /** + * The migration. + * + * @param \Drupal\helfi_api_base\Entity\RemoteEntityBase $entity + * The entity. + * + * @return \Drupal\migrate\Plugin\MigrationInterface + * The migration. + */ + protected function getMigration(RemoteEntityBase $entity) : MigrationInterface { + $definition = $this->entityTypeManager + ->getDefinition($this->getPluginDefinition()['type']) + ->getClass()::getMigration(); + + return $this->migrationPluginManager->createInstance($definition, [ + 'entity_ids' => [$entity->id()], + ]); + } + + /** + * {@inheritdoc} + */ + public function execute($entity = NULL) { + if (!$entity instanceof RemoteEntityBase) { + throw new \InvalidArgumentException('Given entity is not instanceof RemoteEntityBase.'); + } + $this->setIsPartialMigrate(TRUE); + $migration = $this->getMigration($entity); + $keys = array_keys($migration->getSourcePlugin()->getIds()); + $migration->getIdMap()->setUpdate(array_combine($keys, [$entity->id()])); + + $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable->import(); + } + + /** + * {@inheritdoc} + */ + public function access( + $object, + AccountInterface $account = NULL, + $return_as_object = FALSE + ) { + /** @var \Drupal\helfi_api_base\Entity\RemoteEntityBase $object */ + $access = $object->access('update', NULL, TRUE); + + return $return_as_object ? $access : $access->isAllowed(); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + $module_name = $this->entityTypeManager + ->getDefinition($this->getPluginDefinition()['type']) + ->getProvider(); + return ['module' => [$module_name]]; + } + +} diff --git a/src/Plugin/Derivative/MigrationUpdateActionDerivative.php b/src/Plugin/Derivative/MigrationUpdateActionDerivative.php new file mode 100644 index 00000000..97efa22d --- /dev/null +++ b/src/Plugin/Derivative/MigrationUpdateActionDerivative.php @@ -0,0 +1,65 @@ +entityTypeManager = $container->get('entity_type.manager'); + + return $instance; + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + if (empty($this->derivatives)) { + $definitions = []; + + $entity_types = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entityType) { + if (!$entityType->entityClassImplements(RemoteEntityBase::class)) { + return FALSE; + } + return $entityType->getClass()::getMigration() ?? FALSE; + }); + + foreach ($entity_types as $entity_type_id => $entity_type) { + $definition = $base_plugin_definition; + $definition['type'] = $entity_type_id; + $definition['label'] = sprintf('%s %s', $base_plugin_definition['action_label'], $entity_type->getSingularLabel()); + $definitions[$entity_type_id] = $definition; + } + $this->derivatives = $definitions; + } + + return parent::getDerivativeDefinitions($base_plugin_definition); + } + +} diff --git a/src/Plugin/migrate/source/HttpSourcePluginBase.php b/src/Plugin/migrate/source/HttpSourcePluginBase.php new file mode 100644 index 00000000..d2448de6 --- /dev/null +++ b/src/Plugin/migrate/source/HttpSourcePluginBase.php @@ -0,0 +1,141 @@ +httpClient->request('GET', $url)->getBody(); + return \GuzzleHttp\json_decode($content, TRUE); + } + catch (GuzzleException $e) { + } + return []; + } + + /** + * Initializes the list iterator. + * + * @return \Iterator + * The iterator. + */ + abstract protected function initializeListIterator() : \Iterator; + + /** + * Initializes iterator with set of entity IDs. + * + * @return \Iterator + * The iterator. + */ + protected function initializeSingleImportIterator() : \Iterator { + foreach ($this->entityIds as $entityId) { + yield $this->getContent($this->buildCanonicalUrl($entityId)); + } + } + + /** + * {@inheritdoc} + */ + protected function initializeIterator() { + if ($this->entityIds) { + return $this->initializeSingleImportIterator(); + } + return $this->initializeListIterator(); + } + + /** + * Builds a canonical url to individual entity. + * + * @param string $id + * The entity ID. + * + * @return string + * The url to canonical page of given entity. + */ + protected function buildCanonicalUrl(string $id) : string { + $urlParts = UrlHelper::parse($this->configuration['url']); + + return vsprintf('%s/%s/?%s', [ + rtrim($urlParts['path'], '/'), + $id, + UrlHelper::buildQuery($urlParts['query']), + ]); + } + + /** + * {@inheritdoc} + */ + public static function create( + ContainerInterface $container, + array $configuration, + $plugin_id, + $plugin_definition, + MigrationInterface $migration = NULL + ) { + $instance = new static($configuration, $plugin_id, $plugin_definition, $migration); + $instance->httpClient = $container->get('http_client'); + + if (!isset($configuration['url'])) { + throw new \InvalidArgumentException('The "url" configuration missing.'); + } + + // Allow certain entity IDs to be updated. + if (isset($migration->entity_ids)) { + $instance->entityIds = $migration->entity_ids; + } + return $instance; + } + +} diff --git a/tests/src/Functional/MigrationTestBase.php b/tests/src/Functional/MigrationTestBase.php new file mode 100644 index 00000000..9cb3bb55 --- /dev/null +++ b/tests/src/Functional/MigrationTestBase.php @@ -0,0 +1,40 @@ +save(); + } + } + +} diff --git a/tests/src/Kernel/ApiKernelTestBase.php b/tests/src/Kernel/ApiKernelTestBase.php index 6fea458e..57906762 100644 --- a/tests/src/Kernel/ApiKernelTestBase.php +++ b/tests/src/Kernel/ApiKernelTestBase.php @@ -5,15 +5,15 @@ namespace Drupal\Tests\helfi_api_base\Kernel; use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; -use GuzzleHttp\Client; -use GuzzleHttp\Handler\MockHandler; -use GuzzleHttp\HandlerStack; +use Drupal\Tests\helfi_api_base\Traits\ApiTestTrait; /** * API test base. */ abstract class ApiKernelTestBase extends EntityKernelTestBase { + use ApiTestTrait; + /** * {@inheritdoc} */ @@ -31,56 +31,4 @@ protected function setUp() : void { $this->installConfig(['helfi_api_base']); } - /** - * Creates HTTP client stub. - * - * @param \Psr\Http\Message\ResponseInterface[] $responses - * The expected responses. - * - * @return \GuzzleHttp\Client - * The client. - */ - protected function createMockHttpClient(array $responses) : Client { - $mock = new MockHandler($responses); - $handlerStack = HandlerStack::create($mock); - - return new Client(['handler' => $handlerStack]); - } - - /** - * Gets the fixture path. - * - * @param string $module - * The module. - * @param string $name - * The name. - * - * @return string - * The fixture path. - */ - protected function getFixturePath(string $module, string $name) : string { - $file = sprintf('%s/tests/fixtures/%s', drupal_get_path('module', $module), $name); - - if (!file_exists($file)) { - throw new \InvalidArgumentException(sprintf('Fixture %s not found', $name)); - } - - return $file; - } - - /** - * Gets the fixture data. - * - * @param string $module - * The module. - * @param string $name - * The fixture name. - * - * @return string - * The fixture. - */ - protected function getFixture(string $module, string $name) : string { - return file_get_contents($this->getFixturePath($module, $name)); - } - } diff --git a/tests/src/Kernel/MigrationTestBase.php b/tests/src/Kernel/MigrationTestBase.php index dc0c41c9..49d8834a 100644 --- a/tests/src/Kernel/MigrationTestBase.php +++ b/tests/src/Kernel/MigrationTestBase.php @@ -5,14 +5,16 @@ namespace Drupal\Tests\helfi_api_base\Kernel; use Drupal\language\Entity\ConfigurableLanguage; -use Drupal\migrate\MigrateExecutable; use Drupal\migrate\MigrateMessageInterface; +use Drupal\Tests\helfi_api_base\Traits\MigrationTestTrait; /** * Base class for multilingual migration tests. */ abstract class MigrationTestBase extends ApiKernelTestBase implements MigrateMessageInterface { + use MigrationTestTrait; + /** * {@inheritdoc} */ @@ -23,16 +25,6 @@ abstract class MigrationTestBase extends ApiKernelTestBase implements MigrateMes 'content_translation', ]; - /** - * A two dimensional array of messages. - * - * The first key is the type of message, the second is just numeric. Values - * are the messages. - * - * @var array - */ - protected ?array $migrateMessages = []; - /** * {@inheritdoc} */ @@ -46,43 +38,4 @@ public function setUp() : void { } } - /** - * Flushes all plugin caches. - */ - protected function flushPluginCache() : void { - $this->container->get('plugin.cache_clearer')->clearCachedDefinitions(); - } - - /** - * {@inheritdoc} - */ - public function display($message, $type = 'status') { - $this->assert($type == 'status', $message, 'migrate'); - } - - /** - * Executes a single migration. - * - * @param string $migration - * The migration ID. - */ - protected function executeMigration(string $migration) { - $migration = $this->getMigration($migration); - - (new MigrateExecutable($migration, $this))->import(); - } - - /** - * Gets the migration plugin. - * - * @param string $plugin_id - * The plugin ID of the migration to get. - * - * @return \Drupal\migrate\Plugin\Migration - * The migration plugin. - */ - protected function getMigration(string $plugin_id) { - return $this->container->get('plugin.manager.migration')->createInstance($plugin_id); - } - } diff --git a/tests/src/Traits/ApiTestTrait.php b/tests/src/Traits/ApiTestTrait.php new file mode 100644 index 00000000..9f282e79 --- /dev/null +++ b/tests/src/Traits/ApiTestTrait.php @@ -0,0 +1,68 @@ + $handlerStack]); + } + + /** + * Gets the fixture path. + * + * @param string $module + * The module. + * @param string $name + * The name. + * + * @return string + * The fixture path. + */ + protected function getFixturePath(string $module, string $name) : string { + $file = sprintf('%s/tests/fixtures/%s', drupal_get_path('module', $module), $name); + + if (!file_exists($file)) { + throw new \InvalidArgumentException(sprintf('Fixture %s not found', $name)); + } + + return $file; + } + + /** + * Gets the fixture data. + * + * @param string $module + * The module. + * @param string $name + * The fixture name. + * + * @return string + * The fixture. + */ + protected function getFixture(string $module, string $name) : string { + return file_get_contents($this->getFixturePath($module, $name)); + } + +} diff --git a/tests/src/Traits/MigrationTestTrait.php b/tests/src/Traits/MigrationTestTrait.php new file mode 100644 index 00000000..5e031279 --- /dev/null +++ b/tests/src/Traits/MigrationTestTrait.php @@ -0,0 +1,63 @@ +container->get('plugin.cache_clearer')->clearCachedDefinitions(); + } + + /** + * {@inheritdoc} + */ + public function display($message, $type = 'status') { + $this->assert($type == 'status', $message, 'migrate'); + } + + /** + * Executes a single migration. + * + * @param string $migration + * The migration ID. + */ + protected function executeMigration(string $migration) { + $migration = $this->getMigration($migration); + + (new MigrateExecutable($migration, $this))->import(); + } + + /** + * Gets the migration plugin. + * + * @param string $plugin_id + * The plugin ID of the migration to get. + * + * @return \Drupal\migrate\Plugin\Migration + * The migration plugin. + */ + protected function getMigration(string $plugin_id) { + return $this->container->get('plugin.manager.migration')->createInstance($plugin_id); + } + +}