diff --git a/modules/stanford_person/modules/stanford_person_importer/src/Config/ConfigOverrides.php b/modules/stanford_person/modules/stanford_person_importer/src/Config/ConfigOverrides.php index 525b101d..79447cce 100644 --- a/modules/stanford_person/modules/stanford_person_importer/src/Config/ConfigOverrides.php +++ b/modules/stanford_person/modules/stanford_person_importer/src/Config/ConfigOverrides.php @@ -4,12 +4,8 @@ use Drupal\config_pages\ConfigPagesLoaderServiceInterface; use Drupal\Core\Cache\CacheableMetadata; -use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryOverrideInterface; use Drupal\Core\Config\StorageInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Url; -use Drupal\stanford_person_importer\CapInterface; /** * Configuration overrides for stanford person importer migration entity. @@ -18,11 +14,6 @@ */ class ConfigOverrides implements ConfigFactoryOverrideInterface { - /** - * How many profiles are returned in each url. - */ - const URL_CHUNKS = 15; - /** * Config pages loader service. * @@ -30,44 +21,14 @@ class ConfigOverrides implements ConfigFactoryOverrideInterface { */ protected $configPages; - /** - * Entity type manager service. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * Cap API service. - * - * @var \Drupal\stanford_person_importer\CapInterface - */ - protected $cap; - - /** - * Config factory service. - * - * @var \Drupal\Core\Config\ConfigFactoryInterface - */ - protected $configFactory; - /** * ConfigOverrides constructor. * * @param \Drupal\config_pages\ConfigPagesLoaderServiceInterface $config_pages * Config pages loader service. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * Entity type manager service. - * @param \Drupal\stanford_person_importer\CapInterface $cap - * Cap API service. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * Config factory service. */ - public function __construct(ConfigPagesLoaderServiceInterface $config_pages, EntityTypeManagerInterface $entity_type_manager, CapInterface $cap, ConfigFactoryInterface $config_factory) { + public function __construct(protected ConfigPagesLoaderServiceInterface $config_pages) { $this->configPages = $config_pages; - $this->entityTypeManager = $entity_type_manager; - $this->cap = $cap; - $this->configFactory = $config_factory; } /** @@ -97,156 +58,13 @@ public function getCacheableMetadata($name) { public function loadOverrides($names) { $overrides = []; if (in_array('migrate_plus.migration.su_stanford_person', $names)) { - $urls = $this->getImporterUrls(); - - if ($urls === NULL) { - $overrides['migrate_plus.migration.su_stanford_person']['status'] = FALSE; - return $overrides; - } - - $overrides['migrate_plus.migration.su_stanford_person']['source']['urls'] = $urls; + $overrides['migrate_plus.migration.su_stanford_person']['source']['plugin'] = 'cap_url'; $overrides['migrate_plus.migration.su_stanford_person']['source']['authentication']['client_id'] = $this->getCapClientId(); $overrides['migrate_plus.migration.su_stanford_person']['source']['authentication']['client_secret'] = $this->getCapClientSecret(); - } - return $overrides; - } - - /** - * Get a list of urls for the importer. - * - * @return array|null - * Array of urls or NULL if any errors occur. - */ - protected function getImporterUrls(): ?array { - $urls = &drupal_static('cap_source_urls'); - if ($urls !== NULL) { - return $urls; - } - try { - $this->cap->setClientId($this->getCapClientId()); - $this->cap->setClientSecret($this->getCapClientSecret()); - - $urls = $this->getOrgsUrls(); - $urls = array_merge($urls, $this->getWorkgroupUrls()); - $urls = array_merge($urls, $this->getSunetUrls()); - } - catch (\Exception $e) { - return NULL; - } - - $allowed_fields = $this->getAllowedFields(); - foreach ($urls as &$url) { - $url = Url::fromUri($url); - $url->mergeOptions(['query' => ['whitelist' => implode(',', $allowed_fields)]]); - $url = $url->toString(TRUE)->getGeneratedUrl(); - } - return $urls; - } - - /** - * Get a list of the fields from CAP that should be fetched. - * - * @return string[] - * Array of CAP selectors. - */ - protected function getAllowedFields() { - $allowed_fields = $this->configFactory->getEditable('migrate_plus.migration.su_stanford_person') - ->getOriginal('source.fields') ?: []; - foreach ($allowed_fields as &$field) { - $field = $field['selector']; - if ($slash_position = strpos($field, '/')) { - $field = substr($field, 0, $slash_position); - } - } - return $allowed_fields; - } - - /** - * Get a list of CAP urls that have an org code specified. - * - * @return string[] - * List of urls. - * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - */ - protected function getOrgsUrls() { - $org_tids = array_filter($this->configPages->getValue('stanford_person_importer', 'su_person_orgs', [], 'target_id') ?? []); - $include_children = (bool) $this->configPages->getValue('stanford_person_importer', 'su_person_child_orgs', 0, 'value'); - - // No field values populated. - if (empty($org_tids)) { - return []; - } - $org_codes = []; - - // Load the taxonomy term that the field is pointing at and use the org code - // field on the term entity. - $term_storage = $this->entityTypeManager->getStorage('taxonomy_term'); - foreach ($org_tids as &$tid) { - if ($term = $term_storage->load($tid)) { - $org_code = $term->get('su_cap_org_code') - ->getString(); - $org_codes[] = str_replace(' ', '', $org_code); - } - } - return $this->getUrlChunks($this->cap->getOrganizationUrl($org_codes, $include_children)); - } - - /** - * Get a list of CAP urls that have a workgroup filter. - * - * @return string[] - * List of urls. - */ - protected function getWorkgroupUrls(): array { - $workgroups = array_filter($this->configPages->getValue('stanford_person_importer', 'su_person_workgroup', [], 'value') ?? []); - if ($workgroups) { - return $this->getUrlChunks($this->cap->getWorkgroupUrl($workgroups)); + $overrides['migrate_plus.migration.su_stanford_person']['status'] = $this->getCapClientId() && $this->getCapClientSecret(); } - return []; - } - - /** - * Get the list of CAP urls for a sunetid filter. - * - * @return string[] - * List of urls. - */ - protected function getSunetUrls(): array { - $sunets = $this->configPages->getValue('stanford_person_importer', 'su_person_sunetid', [], 'value') ?: []; - - $urls = []; - foreach (array_chunk($sunets, self::URL_CHUNKS) as $chunk) { - $urls[] = $this->cap->getSunetUrl($chunk)->toString(TRUE)->getGeneratedUrl(); - } - return $urls; - } - - /** - * Break up the url into multiple urls based on the number of results. - * - * @param \Drupal\Core\Url $url - * Cap API Url. - * - * @return string[] - * Array of Cap API Urls. - */ - protected function getUrlChunks(Url $url): array { - $count = $this->cap->getTotalProfileCount($url); - $number_chunks = ceil($count / self::URL_CHUNKS); - if ($number_chunks <= 1) { - $url->mergeOptions(['query' => ['ps' => self::URL_CHUNKS]]); - return [$url->toString(TRUE)->getGeneratedUrl()]; - } - - $urls = []; - for ($i = 1; $i <= $number_chunks; $i++) { - $url->mergeOptions(['query' => ['p' => $i, 'ps' => self::URL_CHUNKS]]); - $urls[] = $url->toString(TRUE)->getGeneratedUrl(); - } - return $urls; + return $overrides; } /** diff --git a/modules/stanford_person/modules/stanford_person_importer/src/Plugin/migrate/source/CapUrl.php b/modules/stanford_person/modules/stanford_person_importer/src/Plugin/migrate/source/CapUrl.php new file mode 100644 index 00000000..d3ca2103 --- /dev/null +++ b/modules/stanford_person/modules/stanford_person_importer/src/Plugin/migrate/source/CapUrl.php @@ -0,0 +1,207 @@ +<?php + +namespace Drupal\stanford_person_importer\Plugin\migrate\source; + +use Drupal\config_pages\ConfigPagesLoaderServiceInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Url; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate_plus\Plugin\migrate\source\Url as UrlPlugin; +use Drupal\stanford_person_importer\CapInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Source plugin for retrieving data via CAP URLs. + * + * @MigrateSource( + * id = "cap_url" + * ) + */ +class CapUrl extends UrlPlugin implements ContainerFactoryPluginInterface { + + const URL_CHUNKS = 15; + + /** + * {@inheritDoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $migration, + $container->get('stanford_person_importer.cap'), + $container->get('config_pages.loader'), + $container->get('config.factory'), + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritDoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, protected CapInterface $cap, protected ConfigPagesLoaderServiceInterface $configPages, protected ConfigFactoryInterface $configFactory, protected EntityTypeManagerInterface $entityTypeManager) { + $this->sourceUrls = $this->getImporterUrls(); + $configuration['urls'] = $this->sourceUrls; + parent::__construct($configuration, $plugin_id, $plugin_definition, $migration); + } + + /** + * Get a list of urls for the importer. + * + * @return array|null + * Array of urls or NULL if any errors occur. + */ + protected function getImporterUrls(): array { + $urls = &drupal_static('cap_source_urls'); + if ($urls !== NULL) { + return $urls; + } + try { + $this->cap->setClientId($this->getCapClientId()); + $this->cap->setClientSecret($this->getCapClientSecret()); + + $urls = $this->getOrgsUrls(); + $urls = array_merge($urls, $this->getWorkgroupUrls()); + $urls = array_merge($urls, $this->getSunetUrls()); + } + catch (\Exception $e) { + return []; + } + + $allowed_fields = $this->getAllowedFields(); + foreach ($urls as &$url) { + $url = Url::fromUri($url); + $url->mergeOptions(['query' => ['whitelist' => implode(',', $allowed_fields)]]); + $url = $url->toString(TRUE)->getGeneratedUrl(); + } + return $urls; + } + + /** + * Get a list of the fields from CAP that should be fetched. + * + * @return string[] + * Array of CAP selectors. + */ + protected function getAllowedFields() { + $allowed_fields = $this->configFactory->get('migrate_plus.migration.su_stanford_person') + ->get('source.fields') ?: []; + foreach ($allowed_fields as &$field) { + $field = $field['selector']; + if ($slash_position = strpos($field, '/')) { + $field = substr($field, 0, $slash_position); + } + } + return $allowed_fields; + } + + /** + * Get a list of CAP urls that have an org code specified. + * + * @return string[] + * List of urls. + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + protected function getOrgsUrls() { + $org_tids = array_filter($this->configPages->getValue('stanford_person_importer', 'su_person_orgs', [], 'target_id') ?? []); + $include_children = (bool) $this->configPages->getValue('stanford_person_importer', 'su_person_child_orgs', 0, 'value'); + + // No field values populated. + if (empty($org_tids)) { + return []; + } + $org_codes = []; + + // Load the taxonomy term that the field is pointing at and use the org code + // field on the term entity. + $term_storage = $this->entityTypeManager->getStorage('taxonomy_term'); + foreach ($org_tids as &$tid) { + if ($term = $term_storage->load($tid)) { + $org_code = $term->get('su_cap_org_code') + ->getString(); + $org_codes[] = str_replace(' ', '', $org_code); + } + } + return $this->getUrlChunks($this->cap->getOrganizationUrl($org_codes, $include_children)); + } + + /** + * Get a list of CAP urls that have a workgroup filter. + * + * @return string[] + * List of urls. + */ + protected function getWorkgroupUrls(): array { + $workgroups = array_filter($this->configPages->getValue('stanford_person_importer', 'su_person_workgroup', [], 'value') ?? []); + return $workgroups ? $this->getUrlChunks($this->cap->getWorkgroupUrl($workgroups)) : []; + } + + /** + * Get the list of CAP urls for a sunetid filter. + * + * @return string[] + * List of urls. + */ + protected function getSunetUrls(): array { + $sunets = $this->configPages->getValue('stanford_person_importer', 'su_person_sunetid', [], 'value') ?: []; + + $urls = []; + foreach (array_chunk($sunets, self::URL_CHUNKS) as $chunk) { + $urls[] = $this->cap->getSunetUrl($chunk) + ->toString(TRUE) + ->getGeneratedUrl(); + } + return $urls; + } + + /** + * Break up the url into multiple urls based on the number of results. + * + * @param \Drupal\Core\Url $url + * Cap API Url. + * + * @return string[] + * Array of Cap API Urls. + */ + protected function getUrlChunks(Url $url): array { + $count = $this->cap->getTotalProfileCount($url); + $number_chunks = ceil($count / self::URL_CHUNKS); + if ($number_chunks <= 1) { + $url->mergeOptions(['query' => ['ps' => self::URL_CHUNKS]]); + return [$url->toString(TRUE)->getGeneratedUrl()]; + } + + $urls = []; + for ($i = 1; $i <= $number_chunks; $i++) { + $url->mergeOptions(['query' => ['p' => $i, 'ps' => self::URL_CHUNKS]]); + $urls[] = $url->toString(TRUE)->getGeneratedUrl(); + } + return $urls; + } + + /** + * Get the username from the config pages field. + * + * @return string + * Client ID string. + */ + protected function getCapClientId(): string { + return $this->configPages->getValue('stanford_person_importer', 'su_person_cap_username', 0, 'value') ?? ''; + } + + /** + * Get the password from the config pages field. + * + * @return string + * Client secret string. + */ + protected function getCapClientSecret(): string { + return $this->configPages->getValue('stanford_person_importer', 'su_person_cap_password', 0, 'value') ?? ''; + } + +} diff --git a/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Config/ConfigOverridesTest.php b/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Config/ConfigOverridesTest.php index fb31dc07..4d76d3cc 100644 --- a/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Config/ConfigOverridesTest.php +++ b/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Config/ConfigOverridesTest.php @@ -106,8 +106,6 @@ protected function getCapService() { ->willReturn(Url::fromUri('http://localhost.orgs')); $cap->method('getWorkgroupUrl') ->willReturn(Url::fromUri('http://localhost.workgroup')); - $cap->method('getSunetUrl') - ->will($this->returnCallback([$this, 'getSunetUrlCallback'])); return $cap; } @@ -127,13 +125,12 @@ public function testBasicMethods() { } /** - * Test the config overrides when theres no urls. + * Test the config overrides when there's no urls. */ public function testEmptyConfigOverrides() { $overrides = $this->configOverrides->loadOverrides(['migrate_plus.migration.su_stanford_person']); $this->assertEmpty($overrides['migrate_plus.migration.su_stanford_person']['source']['authentication']['client_id']); $this->assertEmpty($overrides['migrate_plus.migration.su_stanford_person']['source']['authentication']['client_secret']); - $this->assertEmpty($overrides['migrate_plus.migration.su_stanford_person']['source']['urls']); } /** @@ -156,38 +153,10 @@ public function testConfigOverrides() { } }); - $this->configOverrides->loadOverrides(['migrate_plus.migration.su_stanford_person']); - - drupal_static_reset('cap_source_urls'); - $overrides = $this->configOverrides->loadOverrides(['migrate_plus.migration.su_stanford_person']); - - $expected_urls = [ - 'http://localhost.orgs?ps=15&whitelist=fooBar,barFoo', - 'http://localhost.workgroup?p=1&ps=15&whitelist=fooBar,barFoo', - 'http://localhost.workgroup?p=2&ps=15&whitelist=fooBar,barFoo', - 'http://localhost.sunet?whitelist=fooBar,barFoo', - ]; - asort($expected_urls); - asort($overrides['migrate_plus.migration.su_stanford_person']['source']['urls']); - foreach ($overrides['migrate_plus.migration.su_stanford_person']['source']['urls'] as &$url) { - $url = urldecode($url); - } - $this->assertEquals(array_values($expected_urls), array_values($overrides['migrate_plus.migration.su_stanford_person']['source']['urls'])); - - $this->sunetUrlError = TRUE; - drupal_static_reset('cap_source_urls'); $overrides = $this->configOverrides->loadOverrides(['migrate_plus.migration.su_stanford_person']); - $this->assertFalse($overrides['migrate_plus.migration.su_stanford_person']['status']); - } - /** - * Cap mock service callback. - */ - public function getSunetUrlCallback() { - if ($this->sunetUrlError) { - throw new \Exception('Error getting sunet url'); - } - return Url::fromUri('http://localhost.sunet'); + $this->assertEquals('foo', $overrides['migrate_plus.migration.su_stanford_person']['source']['authentication']['client_id']); + $this->assertEquals('bar', $overrides['migrate_plus.migration.su_stanford_person']['source']['authentication']['client_secret']); } } diff --git a/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Plugin/migrate/source/CapUrlTest.php b/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Plugin/migrate/source/CapUrlTest.php new file mode 100644 index 00000000..45f2df50 --- /dev/null +++ b/modules/stanford_person/modules/stanford_person_importer/tests/src/Unit/Plugin/migrate/source/CapUrlTest.php @@ -0,0 +1,119 @@ +<?php + +namespace Drupal\Tests\stanford_person_importer\Unit\Plugin\migrate\source; + +use Drupal\config_pages\ConfigPagesLoaderServiceInterface; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; +use Drupal\Core\Url; +use Drupal\Core\Utility\UnroutedUrlAssembler; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\stanford_person_importer\CapInterface; +use Drupal\stanford_person_importer\Plugin\migrate\source\CapUrl; +use Drupal\Tests\UnitTestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +class CapUrlTest extends UnitTestCase { + + protected $plugin; + + protected $profileCount = 10; + + public function testUrls() { + $config_pages = $this->createMock(ConfigPagesLoaderServiceInterface::class); + + $config_pages->method('getValue') + ->will($this->returnCallback([$this, 'getConfigPageValue'])); + $entity = $this->createMock(ContentEntityInterface::class); + $field_list = $this->createMock(FieldItemListInterface::class); + $field_list->method('getString')->willReturn('org:code'); + $entity->method('get')->willReturn($field_list); + $entity_storage = $this->createMock(EntityStorageInterface::class); + $entity_storage->method('load')->willReturn($entity); + $entity_type_manager = $this->createMock(EntityTypeManagerInterface::class); + $entity_type_manager->method('getStorage')->willReturn($entity_storage); + $migration = $this->createMock(MigrationInterface::class); + + $container = new ContainerBuilder(); + + $container->set('config_pages.loader', $config_pages); + $container->set('config.factory', $this->getConfigFactoryStub([ + 'migrate_plus.migration.su_stanford_person' => [ + 'source' => [ + 'fields' => [ + ['selector' => 'foo'], + ['selector' => 'bar/bin/foo'], + ['selector' => 'baz'], + ], + ], + ], + ])); + $container->set('entity_type.manager', $entity_type_manager); + $container->set('unrouted_url_assembler', $this->getUrlAssembler()); + + $cap = $this->createMock(CapInterface::class); + $cap->method('getTotalProfileCount') + ->will($this->returnCallback([$this, 'getCapProfileCount'])); + $cap->method('getOrganizationUrl') + ->willReturn(Url::fromUri('http://orgurl')); + $cap->method('getWorkgroupUrl') + ->willReturn(Url::fromUri('http://workgroupurl')); + $cap->method('getSunetUrl') + ->willReturn(Url::fromUri('http://suneturl')); + + $container->set('stanford_person_importer.cap', $cap); + \Drupal::setContainer($container); + + $plugin = TestCapUrl::create($container, [ + 'fields' => [], + 'ids' => [], + ], 'cap_url', [], $migration); + + $this->assertEquals([ + 'http://orgurl?ps=15&whitelist=foo%2Cbar%2Cbaz', + 'http://workgroupurl?p=1&ps=15&whitelist=foo%2Cbar%2Cbaz', + 'http://workgroupurl?p=2&ps=15&whitelist=foo%2Cbar%2Cbaz', + 'http://suneturl?whitelist=foo%2Cbar%2Cbaz', + ], $plugin->getSourceUrls()); + } + + public function getConfigPageValue($bundle, $field, $delta, $key) { + switch ($field) { + case 'su_person_orgs': + return [1, 2, 3]; + case 'su_person_child_orgs': + return FALSE; + case 'su_person_workgroup': + return ['bar:foo', 'bin:foo']; + case 'su_person_sunetid': + return ['foofoofoo']; + } + } + + protected function getUrlAssembler() { + $request_stack = new RequestStack(); + $request_stack->push(new Request()); + $path_processor = $this->createMock(OutboundPathProcessorInterface::class); + return new UnroutedUrlAssembler($request_stack, $path_processor); + } + + public function getCapProfileCount() { + $count = $this->profileCount; + $this->profileCount += 10; + return $count; + } + +} + +class TestCapUrl extends CapUrl { + + public function getSourceUrls() { + return $this->sourceUrls; + } + +}