View source
<?php
declare (strict_types=1);
namespace Drupal\Tests\entity_share_client\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\Url;
use Drupal\entity_share\EntityShareUtility;
use Drupal\entity_share_client\ClientAuthorization\ClientAuthorizationInterface;
use Drupal\entity_share_client\Entity\RemoteInterface;
use Drupal\entity_share_client\ImportContext;
use Drupal\entity_share_test\EntityFieldHelperTrait;
use Drupal\node\NodeInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\entity_share_server\Functional\EntityShareServerRequestTestTrait;
use Drupal\Tests\RandomGeneratorTrait;
use Drupal\user\UserInterface;
use Faker\Factory;
use Faker\Provider\fr_FR\PhoneNumber;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\RequestOptions;
abstract class EntityShareClientFunctionalTestBase extends BrowserTestBase {
use RandomGeneratorTrait;
use EntityShareServerRequestTestTrait;
use EntityFieldHelperTrait;
const IMPORT_CONFIG_ID = 'test_import_config';
public static $modules = [
'basic_auth',
'entity_share_client',
'entity_share_client_remote_manager_test',
'entity_share_server',
'entity_share_test',
'jsonapi_extras',
];
protected $defaultTheme = 'classy';
protected static $entityTypeId = NULL;
protected static $entityBundleId = NULL;
protected static $entityLangcode = NULL;
protected $adminUser;
protected $channelUser;
protected $entityTypeManager;
protected $fileSystem;
protected $streamWrapperManager;
protected $importService;
protected $remoteManager;
protected $faker;
protected $visitedUrlsDuringSetup = [];
protected $remote;
protected $channels = [];
protected $importConfig;
protected $entities = [];
protected $entitiesData;
protected $entityDefinitions;
protected $authPluginManager;
protected $keyValueStore;
protected function setUp() : void {
parent::setUp();
$this->adminUser = $this
->drupalCreateUser($this
->getAdministratorPermissions());
$this->channelUser = $this
->drupalCreateUser($this
->getChannelUserPermissions());
$config = $this->container
->get('config.factory')
->getEditable('jsonapi_extras.settings');
$config
->set('include_count', TRUE);
$config
->save(TRUE);
$this->fileSystem = $this->container
->get('file_system');
$this->streamWrapperManager = $this->container
->get('stream_wrapper_manager');
$this->entityTypeManager = $this->container
->get('entity_type.manager');
$this->entityDefinitions = $this->entityTypeManager
->getDefinitions();
$this->importService = $this->container
->get('entity_share_client.import_service');
$this->remoteManager = $this->container
->get('entity_share_client.remote_manager');
$this->authPluginManager = $this->container
->get('plugin.manager.entity_share_client_authorization');
$this->keyValueStore = $this->container
->get('keyvalue')
->get(ClientAuthorizationInterface::LOCAL_STORAGE_KEY_VALUE_COLLECTION);
$this->faker = Factory::create();
$this->faker
->addProvider(new PhoneNumber($this->faker));
$this
->createRemote($this->channelUser);
$this
->createChannel($this->channelUser);
$this
->createImportConfig();
}
protected function postSetupFixture() {
$this
->prepareContent();
$this
->populateRequestService();
$this
->deleteContent();
}
protected function getAdministratorPermissions() {
return [
'view the administration theme',
'access administration pages',
'administer_channel_entity',
];
}
protected function getChannelUserPermissions() {
return [
'entity_share_server_access_channels',
];
}
protected function getAuthenticationRequestOptions(AccountInterface $account) {
return [
RequestOptions::HEADERS => [
'Authorization' => 'Basic ' . base64_encode($account
->getAccountName() . ':' . $account->passRaw),
],
];
}
protected function createRemote(UserInterface $user) {
$remote_storage = $this->entityTypeManager
->getStorage('remote');
$remote = $remote_storage
->create([
'id' => $this
->randomMachineName(),
'label' => $this
->randomString(),
'url' => $this
->buildUrl('<front>'),
]);
$plugin = $this
->createAuthenticationPlugin($user, $remote);
$remote
->mergePluginConfig($plugin);
$remote
->save();
$this->remote = $remote;
}
protected function createAuthenticationPlugin(UserInterface $user, RemoteInterface $remote) {
$plugin = $this->authPluginManager
->createInstance('basic_auth');
$configuration = $plugin
->getConfiguration();
$credentials = [
'username' => $user
->getAccountName(),
'password' => $user->passRaw,
];
$storage_key = $configuration['uuid'];
$this->keyValueStore
->set($storage_key, $credentials);
$configuration['data'] = [
'credential_provider' => 'entity_share',
'storage_key' => $storage_key,
];
$plugin
->setConfiguration($configuration);
return $plugin;
}
protected function createChannel(UserInterface $user) {
$channel_storage = $this->entityTypeManager
->getStorage('channel');
$channel = $channel_storage
->create([
'id' => static::$entityTypeId . '_' . static::$entityBundleId . '_' . static::$entityLangcode,
'label' => $this
->randomString(),
'channel_entity_type' => static::$entityTypeId,
'channel_bundle' => static::$entityBundleId,
'channel_langcode' => static::$entityLangcode,
'authorized_users' => [
$user
->uuid(),
],
]);
$channel
->save();
$this->channels[$channel
->id()] = $channel;
}
protected function createImportConfig() {
$import_config_storage = $this->entityTypeManager
->getStorage('import_config');
$import_config = $import_config_storage
->create([
'id' => $this::IMPORT_CONFIG_ID,
'label' => $this
->randomString(),
'import_processor_settings' => $this
->getImportConfigProcessorSettings(),
]);
$import_config
->save();
$this->importConfig = $import_config;
}
protected function mergePluginsToImportConfig(array $plugins) {
$processor_settings = $this->importConfig
->get('import_processor_settings');
$processor_settings = array_merge($processor_settings, $plugins);
$this->importConfig
->set('import_processor_settings', $processor_settings);
$this->importConfig
->save();
}
protected function removePluginFromImportConfig(string $plugin_id) {
$processor_settings = $this->importConfig
->get('import_processor_settings');
if (isset($processor_settings[$plugin_id])) {
unset($processor_settings[$plugin_id]);
$this->importConfig
->set('import_processor_settings', $processor_settings);
$this->importConfig
->save();
}
}
protected function getImportConfigProcessorSettings() {
return [
'default_data_processor' => [
'weights' => [
'is_entity_importable' => -10,
'post_entity_save' => 0,
'prepare_importable_entity_data' => -100,
],
],
'entity_reference' => [
'max_recursion_depth' => -1,
'weights' => [
'process_entity' => 10,
],
],
];
}
protected function prepareContent() {
$entities_data = $this
->getEntitiesData();
foreach ($entities_data as $entity_type_id => $data_per_languages) {
$entity_storage = $this->entityTypeManager
->getStorage($entity_type_id);
if (!isset($this->entities[$entity_type_id])) {
$this->entities[$entity_type_id] = [];
}
foreach ($data_per_languages as $langcode => $entity_data) {
foreach ($entity_data as $entity_uuid => $entity_data_per_field) {
if (isset($this->entities[$entity_type_id][$entity_uuid])) {
$prepared_entity_data = $this
->prepareEntityData($entity_data_per_field);
$entity = $this->entities[$entity_type_id][$entity_uuid];
$entity
->addTranslation($langcode, $prepared_entity_data);
$entity
->save();
}
else {
$entity_data_per_field += [
'langcode' => [
'value' => $langcode,
'checker_callback' => 'getValue',
],
'uuid' => [
'value' => $entity_uuid,
'checker_callback' => 'getValue',
],
];
$prepared_entity_data = $this
->prepareEntityData($entity_data_per_field);
$entity = $entity_storage
->create($prepared_entity_data);
$entity
->save();
}
$this->entities[$entity_type_id][$entity_uuid] = $entity;
}
}
}
}
protected function prepareEntityData(array $entityData) {
$prepared_entity_data = [];
foreach ($entityData as $field_machine_name => $data) {
if (isset($data['value_callback'])) {
$prepared_entity_data[$field_machine_name] = call_user_func($data['value_callback']);
}
else {
$prepared_entity_data[$field_machine_name] = $data['value'];
}
}
return $prepared_entity_data;
}
protected abstract function getEntitiesDataArray();
protected function getEntitiesData() {
if (!isset($this->entitiesData)) {
$this->entitiesData = $this
->getEntitiesDataArray();
}
return $this->entitiesData;
}
protected function populateRequestService() {
$entity_share_entrypoint_url = Url::fromRoute('entity_share_server.resource_list');
$response = $this->remoteManager
->jsonApiRequest($this->remote, 'GET', $entity_share_entrypoint_url
->setAbsolute()
->toString());
$json_response = Json::decode((string) $response
->getBody());
foreach ($json_response['data']['channels'] as $channel_data) {
$this
->discoverJsonApiEndpoints($channel_data['url']);
$this
->discoverJsonApiEndpoints($channel_data['url_uuid']);
}
}
protected function discoverJsonApiEndpoints($url) {
if (in_array($url, $this->visitedUrlsDuringSetup)) {
return;
}
$this->visitedUrlsDuringSetup[] = $url;
$response = $this->remoteManager
->jsonApiRequest($this->remote, 'GET', $url);
$json_response = Json::decode((string) $response
->getBody());
if (is_array($json_response['data'])) {
foreach (EntityShareUtility::prepareData($json_response['data']) as $data) {
if (isset($data['relationships'])) {
foreach ($data['relationships'] as $field_data) {
if ($field_data['data'] == NULL || empty($field_data['data'])) {
continue;
}
$prepared_field_data = EntityShareUtility::prepareData($field_data['data']);
$config_or_user = FALSE;
foreach ($prepared_field_data as $field_data_value) {
$parsed_type = explode('--', $field_data_value['type']);
$entity_type_id = $parsed_type[0];
if ($entity_type_id == 'user') {
$config_or_user = TRUE;
break;
}
elseif ($this->entityDefinitions[$entity_type_id]
->getGroup() == 'configuration') {
$config_or_user = TRUE;
break;
}
}
if ($config_or_user) {
continue;
}
if (isset($field_data['links']['related']['href'])) {
$this
->discoverJsonApiEndpoints($field_data['links']['related']['href']);
}
}
}
if ($data['type'] == 'file--file' && isset($data['attributes']['uri']['url'])) {
try {
$this->remoteManager
->request($this->remote, 'GET', $data['attributes']['uri']['url']);
} catch (ClientException $exception) {
}
}
}
}
if (isset($json_response['links']['next']['href'])) {
$this
->discoverJsonApiEndpoints($json_response['links']['next']['href']);
}
}
protected function deleteContent() {
foreach ($this->entities as $entity_type_id => $entity_list) {
$entity_storage = $this->entityTypeManager
->getStorage($entity_type_id);
foreach ($entity_list as $entity_uuid => $entity) {
$entity
->delete();
$remaining_entities = $entity_storage
->loadByProperties([
'uuid' => $entity_uuid,
]);
$this
->assertTrue(empty($remaining_entities), 'The ' . $entity_type_id . ' with UUID ' . $entity_uuid . ' has been deleted.');
}
}
}
protected function resetImportedContent() {
$entity_type_ids = array_keys($this
->getEntitiesDataArray());
foreach ($entity_type_ids as $entity_type_id) {
$entity_storage = $this->entityTypeManager
->getStorage($entity_type_id);
$entities = $entity_storage
->loadByProperties();
if ($entities) {
foreach ($entities as $entity) {
$entity
->delete();
}
}
}
$this->entities = [];
$this->importService
->getRuntimeImportContext()
->clearImportedEntities();
}
protected function checkCreatedEntities() {
$entities_data = $this
->getEntitiesData();
foreach ($entities_data as $entity_type_id => $data_per_languages) {
$entity_storage = $this->entityTypeManager
->getStorage($entity_type_id);
foreach ($data_per_languages as $language_id => $entity_data) {
foreach ($entity_data as $entity_uuid => $entity_data_per_field) {
$recreated_entities = $entity_storage
->loadByProperties([
'uuid' => $entity_uuid,
]);
$this
->assertTrue(!empty($recreated_entities), 'The ' . $entity_type_id . ' with UUID ' . $entity_uuid . ' has been recreated.');
if (!empty($recreated_entities)) {
$recreated_entity = array_shift($recreated_entities);
$entity_translation = $recreated_entity
->getTranslation($language_id);
foreach ($entity_data_per_field as $field_machine_name => $data) {
if (isset($data['value_callback'])) {
$data['value'] = call_user_func($data['value_callback']);
}
if ($data['checker_callback'] == 'getFilteredStructureValues') {
$structure = array_keys($data['value'][0]);
$this
->assertEquals($data['value'], $this
->getFilteredStructureValues($entity_translation, $field_machine_name, $structure), 'The data of the field ' . $field_machine_name . ' has been recreated.');
}
else {
$this
->assertEquals($data['value'], $this
->{$data['checker_callback']}($entity_translation, $field_machine_name), 'The data of the field ' . $field_machine_name . ' has been recreated.');
}
}
}
}
}
}
}
protected function pullEveryChannels() {
foreach (array_keys($this->channels) as $channel_id) {
$this
->pullChannel($channel_id);
}
}
protected function pullChannel($channel_id) {
$import_context = new ImportContext($this->remote
->id(), $channel_id, $this::IMPORT_CONFIG_ID);
$this->importService
->importChannel($import_context);
$batch =& batch_get();
$batch['progressive'] = FALSE;
batch_process();
}
protected function importSelectedEntities(array $selected_entities, string $channel_id = '') {
$channel_id = !empty($channel_id) ? $channel_id : static::$entityTypeId . '_' . static::$entityBundleId . '_' . static::$entityLangcode;
$prepared_url = $this
->prepareUrlFilteredOnUuids($selected_entities, $channel_id);
$import_context = new ImportContext($this->remote
->id(), $channel_id, $this::IMPORT_CONFIG_ID);
$this->importService
->prepareImport($import_context);
$this->importService
->importFromUrl($prepared_url);
}
protected function getEntityJsonData($channel_id, $entity_uuid) {
$json_data = [];
$channel_infos = $this->remoteManager
->getChannelsInfos($this->remote);
$channel_url = $channel_infos[$channel_id]['url'];
while ($channel_url) {
$response = $this->remoteManager
->jsonApiRequest($this->remote, 'GET', $channel_url);
$json = Json::decode((string) $response
->getBody());
$json_data = EntityShareUtility::prepareData($json['data']);
foreach ($json_data as $entity_json_data) {
if ($entity_json_data['id'] == $entity_uuid) {
return $entity_json_data;
}
}
if (isset($json['links']['next']['href'])) {
$channel_url = $json['links']['next']['href'];
}
else {
$channel_url = FALSE;
}
}
return $json_data;
}
protected function getCompleteMediaInfos(array $media_infos) {
return array_merge([
'status' => [
'value' => NodeInterface::PUBLISHED,
'checker_callback' => 'getValue',
],
], $media_infos);
}
protected function getCompleteNodeInfos(array $node_infos) {
return array_merge([
'type' => [
'value' => static::$entityBundleId,
'checker_callback' => 'getTargetId',
],
'title' => [
'value' => $this
->randomString(),
'checker_callback' => 'getValue',
],
], $node_infos);
}
protected function getCompleteTaxonomyTermInfos(array $taxonomy_term_infos) {
return array_merge([
'vid' => [
'value' => static::$entityBundleId,
'checker_callback' => 'getTargetId',
],
'name' => [
'value' => $this
->randomString(),
'checker_callback' => 'getValue',
],
], $taxonomy_term_infos);
}
protected function getCompleteParagraphInfos(array $paragraph_infos) {
return array_merge([
'type' => [
'value' => 'es_test',
'checker_callback' => 'getTargetId',
],
], $paragraph_infos);
}
protected function getCompleteBlockInfos(array $block_infos) {
return array_merge([
'type' => [
'value' => 'es_test',
'checker_callback' => 'getTargetId',
],
'info' => [
'value' => $this
->randomString(),
'checker_callback' => 'getValue',
],
], $block_infos);
}
protected function getEntityId($entity_type_id, $entity_uuid) {
$existing_entity_id = '';
$existing_entity = $this
->loadEntity($entity_type_id, $entity_uuid);
if (!is_null($existing_entity)) {
$existing_entity_id = $existing_entity
->id();
}
return $existing_entity_id;
}
protected function getEntityRevisionId($entity_type_id, $entity_uuid) {
$existing_entity_id = '';
$existing_entity = $this
->loadEntity($entity_type_id, $entity_uuid);
if (!is_null($existing_entity) && $existing_entity instanceof RevisionableInterface) {
$existing_entity_id = $existing_entity
->getRevisionId();
}
return $existing_entity_id;
}
protected function loadEntity($entity_type_id, $entity_uuid) {
$existing_entity = NULL;
$existing_entities = $this->entityTypeManager
->getStorage($entity_type_id)
->loadByProperties([
'uuid' => $entity_uuid,
]);
if (!empty($existing_entities)) {
$existing_entity = array_shift($existing_entities);
}
return $existing_entity;
}
protected function prepareUrlFilteredOnUuids(array $selected_entities, $channel_id) {
$channel_infos = $this->remoteManager
->getChannelsInfos($this->remote);
$channel_url = $channel_infos[$channel_id]['url'];
return EntityShareUtility::prepareUuidsFilteredUrl($channel_url, array_values($selected_entities));
}
protected function preparePhysicalFilesAndFileEntitiesData() {
$files_entities_data = [];
foreach (static::$filesData as $file_uuid => $file_data) {
$stream_wrapper = $this->streamWrapperManager
->getViaUri($file_data['uri']);
$directory_uri = $stream_wrapper
->dirname($file_data['uri']);
$this->fileSystem
->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY);
if (isset($file_data['file_content'])) {
file_put_contents($file_data['uri'], $file_data['file_content']);
$this->filesSize[$file_uuid] = filesize($file_data['uri']);
}
elseif (isset($file_data['file_content_callback'])) {
$this
->{$file_data['file_content_callback']}($file_uuid, $file_data);
}
$files_entities_data[$file_uuid] = [
'filename' => [
'value' => $file_data['filename'],
'checker_callback' => 'getValue',
],
'uri' => [
'value' => $file_data['uri'],
'checker_callback' => 'getValue',
],
'filemime' => [
'value' => $file_data['filemime'],
'checker_callback' => 'getValue',
],
'status' => [
'value' => FILE_STATUS_PERMANENT,
'checker_callback' => 'getValue',
],
];
}
return $files_entities_data;
}
protected function commonBasicPull() {
foreach (static::$filesData as $file_data) {
$this
->assertFalse(file_exists($file_data['uri']), 'The physical file ' . $file_data['filename'] . ' has been deleted.');
}
$this
->pullEveryChannels();
$this
->checkCreatedEntities();
foreach (static::$filesData as $file_uuid => $file_data) {
$this
->assertTrue(file_exists($file_data['uri']), 'The physical file ' . $file_data['filename'] . ' has been pulled and recreated.');
if (isset($file_data['file_content'])) {
$recreated_file_data = file_get_contents($file_data['uri']);
$this
->assertEquals($file_data['file_content'], $recreated_file_data, 'The recreated physical file ' . $file_data['filename'] . ' has the same content.');
}
if (isset($this->filesSize[$file_uuid])) {
$this
->assertEquals($this->filesSize[$file_uuid], filesize($file_data['uri']), 'The recreated physical file ' . $file_data['filename'] . ' has the same size as the original.');
}
}
}
protected function getMediaEntityReferenceTestFiles($file_uuid, array $file_data) {
$filepath = drupal_get_path('module', 'entity_share') . '/tests/fixtures/files/' . $file_data['filename'];
$this->fileSystem
->copy($filepath, PublicStream::basePath());
$this->filesSize[$file_uuid] = filesize($filepath);
}
}