diff --git a/cypress/integration/09_admin_links.spec.js b/cypress/integration/09_admin_links.spec.js index 2a64b30c90..e73f3272e1 100755 --- a/cypress/integration/09_admin_links.spec.js +++ b/cypress/integration/09_admin_links.spec.js @@ -10,11 +10,11 @@ context('Administration pages', () => { const links = [ 'Datasets', 'Datastore Import Status', - 'Datastore settings', + 'Datastore Settings', 'Data Dictionary', 'Harvests', - 'Metastore settings', - 'Resources' + 'Metastore Settings', + 'Resource Settings' ] cy.visit(`${baseurl}/admin`) @@ -25,19 +25,19 @@ context('Administration pages', () => { }) }) - it('Admin can access the Metastore settings.', () => { + it('Admin can access the Metastore Settings.', () => { cy.get('.toolbar-icon-system-admin-dkan').contains('DKAN').next('.toolbar-menu').then($el=>{ cy.wrap($el).invoke('show') - cy.wrap($el).contains('Metastore settings') + cy.wrap($el).contains('Metastore Settings') }) cy.visit(baseurl + "/admin/dkan/properties") cy.get('.option').should('contain.text', 'Distribution (distribution)') }) - it('Admin can access the Datastore settings.', () => { + it('Admin can access the Datastore Settings.', () => { cy.get('.toolbar-icon-system-admin-dkan').contains('DKAN').next('.toolbar-menu').then($el=>{ cy.wrap($el).invoke('show') - cy.wrap($el).contains('Datastore settings') + cy.wrap($el).contains('Datastore Settings') }) cy.visit(baseurl + "/admin/dkan/datastore") cy.get('label[for="edit-rows-limit"]').should('have.text', 'Rows limit') diff --git a/modules/datastore/config/schema/datastore.schema.yml b/modules/datastore/config/schema/datastore.schema.yml index 25d257b0e3..3d4215b269 100644 --- a/modules/datastore/config/schema/datastore.schema.yml +++ b/modules/datastore/config/schema/datastore.schema.yml @@ -14,3 +14,5 @@ datastore.settings: type: string delete_local_resource: type: boolean + drop_datastore_on_post_import_error: + type: boolean diff --git a/modules/datastore/datastore.links.menu.yml b/modules/datastore/datastore.links.menu.yml index 72c14565c9..c40f789e25 100644 --- a/modules/datastore/datastore.links.menu.yml +++ b/modules/datastore/datastore.links.menu.yml @@ -1,12 +1,12 @@ datastore.settings_form: - title: Datastore settings + title: Datastore Settings description: Datastore settings. parent: system.admin_dkan route_name: datastore.settings weight: 12 datastore.dkan_resource_settings: - title: Resources + title: Resource Settings description: Resource options. parent: system.admin_dkan route_name: datastore.dkan_resource_settings diff --git a/modules/datastore/datastore.routing.yml b/modules/datastore/datastore.routing.yml index 09ac3bd51a..1c0d4fb8b3 100644 --- a/modules/datastore/datastore.routing.yml +++ b/modules/datastore/datastore.routing.yml @@ -150,7 +150,7 @@ datastore.sql_endpoint.post.api: datastore.dkan_resource_settings: path: '/admin/dkan/resources' defaults: - _title: 'Resources' + _title: 'Resource Settings' _form: 'Drupal\datastore\Form\ResourceSettingsForm' requirements: _permission: 'administer site configuration' diff --git a/modules/datastore/src/Form/ResourceSettingsForm.php b/modules/datastore/src/Form/ResourceSettingsForm.php index 2b0e40f541..a74841870e 100644 --- a/modules/datastore/src/Form/ResourceSettingsForm.php +++ b/modules/datastore/src/Form/ResourceSettingsForm.php @@ -54,6 +54,16 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $this->config('datastore.settings')->get('delete_local_resource'), '#description' => $this->t('Delete local copy of remote files after the datastore import is complete'), ]; + $form['drop_datastore_on_post_import_error'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Drop the datastore table if the post import queue reports an error.'), + '#default_value' => $this->config('datastore.settings')->get('drop_datastore_on_post_import_error'), + '#description' => $this->t('The datastore import queue brings in all columns as strings. The post import + queue will alter the table according to the data dictionary, if there is a problem during this step the + error will be posted to the Datastore Import Status dashboard, and the datastore table will keep all + data typed as strings. Check this box if you prefer that the table be dropped if there is a problem + in the post import stage.'), + ]; $form['resource_perspective_display'] = [ '#type' => 'select', '#title' => $this->t('Resource download url display'), @@ -76,6 +86,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { ->set('purge_table', $form_state->getValue('purge_table')) ->set('purge_file', $form_state->getValue('purge_file')) ->set('delete_local_resource', $form_state->getValue('delete_local_resource')) + ->set('drop_datastore_on_post_import_error', $form_state->getValue('drop_datastore_on_post_import_error')) ->save(); $this->config('metastore.settings') ->set('resource_perspective_display', $form_state->getValue('resource_perspective_display')) diff --git a/modules/datastore/src/Plugin/QueueWorker/PostImportResourceProcessor.php b/modules/datastore/src/Plugin/QueueWorker/PostImportResourceProcessor.php index 5218438f54..00ebea47d7 100644 --- a/modules/datastore/src/Plugin/QueueWorker/PostImportResourceProcessor.php +++ b/modules/datastore/src/Plugin/QueueWorker/PostImportResourceProcessor.php @@ -2,11 +2,13 @@ namespace Drupal\datastore\Plugin\QueueWorker; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Queue\QueueWorkerBase; use Drupal\common\DataResource; use Drupal\datastore\DataDictionary\AlterTableQueryBuilderInterface; use Drupal\datastore\PostImportResult; +use Drupal\datastore\DatastoreService; use Drupal\datastore\Service\PostImport; use Drupal\datastore\Service\ResourceProcessor\ResourceDoesNotHaveDictionary; use Drupal\datastore\Service\ResourceProcessorCollector; @@ -30,6 +32,13 @@ */ class PostImportResourceProcessor extends QueueWorkerBase implements ContainerFactoryPluginInterface { + /** + * The datastore.settings config. + * + * @var \Drupal\Core\Config\ImmutableConfig + */ + protected $config; + /** * A logger channel for this plugin. * @@ -51,6 +60,13 @@ class PostImportResourceProcessor extends QueueWorkerBase implements ContainerFa */ protected ResourceProcessorCollector $resourceProcessorCollector; + /** + * The datastore service. + * + * @var \Drupal\datastore\DatastoreService + */ + protected DatastoreService $datastoreService; + /** * The PostImport service. * @@ -81,6 +97,8 @@ class PostImportResourceProcessor extends QueueWorkerBase implements ContainerFa * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. + * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory + * The config.factory service. * @param \Drupal\datastore\DataDictionary\AlterTableQueryBuilderInterface $alter_table_query_builder * The alter table query factory service. * @param \Psr\Log\LoggerInterface $logger_channel @@ -89,6 +107,8 @@ class PostImportResourceProcessor extends QueueWorkerBase implements ContainerFa * The metastore resource mapper service. * @param \Drupal\datastore\Service\ResourceProcessorCollector $processor_collector * The resource processor collector service. + * @param \Drupal\datastore\DatastoreService $datastoreService + * The resource datastore service. * @param \Drupal\datastore\Service\PostImport $post_import * The post import service. * @param \Drupal\metastore\DataDictionary\DataDictionaryDiscoveryInterface $data_dictionary_discovery @@ -100,18 +120,22 @@ public function __construct( array $configuration, $plugin_id, $plugin_definition, + ConfigFactoryInterface $configFactory, AlterTableQueryBuilderInterface $alter_table_query_builder, LoggerInterface $logger_channel, ResourceMapper $resource_mapper, ResourceProcessorCollector $processor_collector, + DatastoreService $datastoreService, PostImport $post_import, DataDictionaryDiscoveryInterface $data_dictionary_discovery, ReferenceLookup $referenceLookup ) { parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->config = $configFactory; $this->logger = $logger_channel; $this->resourceMapper = $resource_mapper; $this->resourceProcessorCollector = $processor_collector; + $this->datastoreService = $datastoreService; $this->postImport = $post_import; $this->dataDictionaryDiscovery = $data_dictionary_discovery; // Set the timeout for database connections to the queue lease time. @@ -130,10 +154,12 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, + $container->get('config.factory'), $container->get('dkan.datastore.data_dictionary.alter_table_query_builder.mysql'), $container->get('dkan.datastore.logger_channel'), $container->get('dkan.metastore.resource_mapper'), $container->get('dkan.datastore.service.resource_processor_collector'), + $container->get('dkan.datastore.service'), $container->get('dkan.datastore.service.post_import'), $container->get('dkan.metastore.data_dictionary_discovery'), $container->get('dkan.metastore.reference_lookup'), @@ -145,6 +171,9 @@ public static function create(ContainerInterface $container, array $configuratio */ public function processItem($data) { $postImportResult = $this->postImportProcessItem($data); + $drop_config = $this->config->get('datastore.settings') + ->get('drop_datastore_on_post_import_error'); + if ($postImportResult->getPostImportStatus() === 'done') { $this->invalidateCacheTags(DataResource::buildUniqueIdentifier( $data->getIdentifier(), @@ -152,6 +181,18 @@ public function processItem($data) { DataResource::DEFAULT_SOURCE_PERSPECTIVE )); } + if ($postImportResult->getPostImportStatus() === 'error' && $drop_config) { + $identifier = $data->getIdentifier(); + try { + $this->datastoreService->drop($identifier, NULL, FALSE); + $this->logger->notice('Successfully dropped the datastore for resource @identifier due to a post import error. Visit the Datastore Import Status dashboard for details.', [ + '@identifier' => $identifier, + ]); + } + catch (\Exception $e) { + $this->logger->error($e->getMessage()); + } + } // Store the results of the PostImportResult object. $postImportResult->storeResult(); } diff --git a/modules/datastore/tests/src/Kernel/Plugin/QueueWorker/PostImportResourceProcessorTest.php b/modules/datastore/tests/src/Kernel/Plugin/QueueWorker/PostImportResourceProcessorTest.php index 6bf16c0446..df8f2d15d0 100644 --- a/modules/datastore/tests/src/Kernel/Plugin/QueueWorker/PostImportResourceProcessorTest.php +++ b/modules/datastore/tests/src/Kernel/Plugin/QueueWorker/PostImportResourceProcessorTest.php @@ -1,9 +1,14 @@ config('datastore.settings') + ->set('drop_datastore_on_post_import_error', TRUE) + ->save(); + + // Our error result. + $error_result = $this->getMockBuilder(PostImportResult::class) + ->disableOriginalConstructor() + ->onlyMethods(['getPostImportStatus', 'storeResult']) + ->getMock(); + $error_result->expects($this->any()) + ->method('getPostImportStatus') + ->willReturn('error'); + $error_result->expects($this->once()) + ->method('storeResult'); + + // Mock a logger to expect error logging. + $logger = $this->getMockBuilder(LoggerChannelInterface::class) + ->onlyMethods(['error', 'notice']) + ->getMockForAbstractClass(); + // Never expect an error. + $logger->expects($this->never()) + ->method('error'); + // Expect one notice. + $logger->expects($this->once()) + ->method('notice') + ->with( + 'Successfully dropped the datastore for resource @identifier due to a post import error. Visit the Datastore Import Status dashboard for details.', + ['@identifier' => $data_identifier], + ); + $this->container->set('dkan.datastore.logger_channel', $logger); + + // Datastore service will always succeed. Mocked so we don't have to deal + // with dropping an actual datastore. + $datastore_service = $this->getMockBuilder(DatastoreService::class) + ->disableOriginalConstructor() + ->onlyMethods(['drop']) + ->getMock(); + $datastore_service->expects($this->once()) + ->method('drop'); + // Put the service into the service container. + $this->container->set('dkan.datastore.service', $datastore_service); + + // Return our error result. + $post_import_resource_processor = $this->getMockBuilder(PostImportResourceProcessor::class) + ->setConstructorArgs([ + [], + '', + ['cron' => ['lease_time' => 10800]], + $this->container->get('config.factory'), + $this->container->get('dkan.datastore.data_dictionary.alter_table_query_builder.mysql'), + $this->container->get('dkan.datastore.logger_channel'), + $this->container->get('dkan.metastore.resource_mapper'), + $this->container->get('dkan.datastore.service.resource_processor_collector'), + $this->container->get('dkan.datastore.service'), + $this->container->get('dkan.datastore.service.post_import'), + $this->container->get('dkan.metastore.data_dictionary_discovery'), + $this->container->get('dkan.metastore.reference_lookup'), + ]) + ->onlyMethods(['postImportProcessItem']) + ->getMock(); + $post_import_resource_processor->expects($this->once()) + ->method('postImportProcessItem') + ->willReturn($error_result); + + // Data we'll pass to our method under test. + $data = $this->getMockBuilder(DataResource::class) + ->disableOriginalConstructor() + ->onlyMethods(['getIdentifier']) + ->getMock(); + $data->expects($this->once()) + ->method('getIdentifier') + ->willReturn($data_identifier); + + $post_import_resource_processor->processItem($data); + } + + /** + * @covers ::processItem + */ + public function testProcessItemExceptionPath() { + $this->config('datastore.settings') + ->set('drop_datastore_on_post_import_error', TRUE) + ->save(); + + // Our error result. + $error_result = $this->getMockBuilder(PostImportResult::class) + ->disableOriginalConstructor() + ->onlyMethods(['getPostImportStatus', 'storeResult']) + ->getMock(); + $error_result->expects($this->any()) + ->method('getPostImportStatus') + ->willReturn('error'); + $error_result->expects($this->once()) + ->method('storeResult'); + + // Mock a logger to expect error logging. + $logger = $this->getMockBuilder(LoggerChannelInterface::class) + ->onlyMethods(['error', 'notice']) + ->getMockForAbstractClass(); + // Expect an error. + $logger->expects($this->once()) + ->method('error'); + // Expect no notices. + $logger->expects($this->never()) + ->method('notice'); + $this->container->set('dkan.datastore.logger_channel', $logger); + + // Datastore service rigged to explode. + $datastore_service = $this->getMockBuilder(DatastoreService::class) + ->disableOriginalConstructor() + ->onlyMethods(['drop']) + ->getMock(); + $datastore_service->expects($this->once()) + ->method('drop') + ->willThrowException(new \Exception('our test message')); + // Put the service into the service container. + $this->container->set('dkan.datastore.service', $datastore_service); + + // Return our error result. + $post_import_resource_processor = $this->getMockBuilder(PostImportResourceProcessor::class) + ->setConstructorArgs([ + [], + '', + ['cron' => ['lease_time' => 10800]], + $this->container->get('config.factory'), + $this->container->get('dkan.datastore.data_dictionary.alter_table_query_builder.mysql'), + $this->container->get('dkan.datastore.logger_channel'), + $this->container->get('dkan.metastore.resource_mapper'), + $this->container->get('dkan.datastore.service.resource_processor_collector'), + $this->container->get('dkan.datastore.service'), + $this->container->get('dkan.datastore.service.post_import'), + $this->container->get('dkan.metastore.data_dictionary_discovery'), + $this->container->get('dkan.metastore.reference_lookup'), + ]) + ->onlyMethods(['postImportProcessItem']) + ->getMock(); + $post_import_resource_processor->expects($this->once()) + ->method('postImportProcessItem') + ->willReturn($error_result); + + // Data we'll pass to our method under test. + $data = $this->getMockBuilder(DataResource::class) + ->disableOriginalConstructor() + ->onlyMethods(['getIdentifier']) + ->getMock(); + $data->expects($this->once()) + ->method('getIdentifier') + ->willReturn('test'); + + $post_import_resource_processor->processItem($data); + } + } diff --git a/modules/datastore/tests/src/Unit/Plugin/QueueWorker/PostImportResourceProcessorTest.php b/modules/datastore/tests/src/Unit/Plugin/QueueWorker/PostImportResourceProcessorTest.php index 5898681aac..3d8551a541 100644 --- a/modules/datastore/tests/src/Unit/Plugin/QueueWorker/PostImportResourceProcessorTest.php +++ b/modules/datastore/tests/src/Unit/Plugin/QueueWorker/PostImportResourceProcessorTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\datastore\Unit\Plugin\QueueWorker; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\Container; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\StreamWrapper\StreamWrapperManager; @@ -9,6 +10,7 @@ use Drupal\datastore\DataDictionary\AlterTableQueryBuilderInterface; use Drupal\datastore\DataDictionary\AlterTableQueryInterface; use Drupal\datastore\Plugin\QueueWorker\PostImportResourceProcessor; +use Drupal\datastore\DatastoreService; use Drupal\datastore\Service\PostImport; use Drupal\datastore\Service\ResourceProcessorCollector; use Drupal\datastore\Service\ResourceProcessorInterface; @@ -24,8 +26,6 @@ use RootedData\RootedJsonData; /** - * Test \Drupal\datastore\Plugin\QueueWorker\PostImportResourceProcessor. - * * @coversDefaultClass \Drupal\datastore\Plugin\QueueWorker\PostImportResourceProcessor * * @group dkan @@ -43,17 +43,19 @@ class PostImportResourceProcessorTest extends TestCase { /** * Test postImportProcessItem() succeeds. + * + * @covers ::postImportProcessItem */ public function testPostImportProcessItem() { $resource = new DataResource('test.csv', 'text/csv'); $dataDictionaryDiscovery = $this->getMockBuilder(DataDictionaryDiscovery::class) - ->onlyMethods(['getDataDictionaryMode']) - ->disableOriginalConstructor() - ->getMock(); + ->onlyMethods(['getDataDictionaryMode']) + ->disableOriginalConstructor() + ->getMock(); $dataDictionaryDiscovery->method('getDataDictionaryMode') - ->willReturn("sitewide"); + ->willReturn("sitewide"); $resource_processor = (new Chain($this)) ->add(ResourceProcessorInterface::class, 'process') @@ -86,17 +88,19 @@ public function testPostImportProcessItem() { /** * Test postImportProcessItem() DataDictionary disabled. + * + * @covers ::postImportProcessItem */ public function testPostImportProcessItemDataDictionaryDisabled() { $resource = new DataResource('test.csv', 'text/csv'); $dataDictionaryDiscovery = $this->getMockBuilder(DataDictionaryDiscovery::class) - ->onlyMethods(['getDataDictionaryMode']) - ->disableOriginalConstructor() - ->getMock(); + ->onlyMethods(['getDataDictionaryMode']) + ->disableOriginalConstructor() + ->getMock(); $dataDictionaryDiscovery->method('getDataDictionaryMode') - ->willReturn("none"); + ->willReturn("none"); $resource_processor = (new Chain($this)) ->add(ResourceProcessorInterface::class, 'process') @@ -124,6 +128,8 @@ public function testPostImportProcessItemDataDictionaryDisabled() { /** * Test postImportProcessItem() halts and logs a message if a resource no longer exists. + * + * @covers ::postImportProcessItem */ public function testPostImportProcessItemResourceNoLongerExists() { $resource = new DataResource('test.csv', 'text/csv'); @@ -156,9 +162,11 @@ public function testPostImportProcessItemResourceNoLongerExists() { $this->assertEmpty($errors); } - // /** - // * Test postImportProcessItem() halts and logs a message if a resource has changed. - // */ + /** + * Test postImportProcessItem() halts, logs message if resource has changed. + * + * @covers ::postImportProcessItem + */ public function testPostImportProcessItemResourceChanged() { $resource_a = new DataResource('test.csv', 'text/csv'); @@ -192,9 +200,11 @@ public function testPostImportProcessItemResourceChanged() { $this->assertEmpty($errors); } - // /** - // * Test postImportProcessItem() logs errors encountered in processors. - // */ + /** + * Test postImportProcessItem() logs errors encountered in processors. + * + * @covers ::postImportProcessItem + */ public function testPostImportProcessItemProcessorError() { $resource = new DataResource('test.csv', 'text/csv'); @@ -226,12 +236,95 @@ public function testPostImportProcessItemProcessorError() { $this->assertEquals($errors[0], 'Test Error'); } + /** + * Verify Datastore Drop on Post-Import Error (with drop_config enabled) + * + * @covers ::postImportProcessItem + */ + public function testDatastoreDropOnPostImportError() { + $resource = new DataResource('test.csv', 'text/csv'); + $resource_processor = (new Chain($this)) + ->add(ResourceProcessorInterface::class, 'process', new \Exception('Test Error')) + ->getMock(); + + $container_chain = $this->getContainerChain() + ->add(ResourceProcessorCollector::class, 'getResourceProcessors', [$resource_processor]) + ->add(ResourceMapper::class, 'get', $resource); + \Drupal::setContainer($container_chain->getMock()); + + $dictionaryEnforcer = PostImportResourceProcessor::create( + $container_chain->getMock(), [], '', ['cron' => ['lease_time' => 10800]] + ); + $postImportResult = $dictionaryEnforcer->postImportProcessItem($resource); + + $this->assertEquals($resource->getIdentifier(), $postImportResult->getResourceIdentifier()); + $this->assertEquals($resource->getVersion(), $postImportResult->getResourceVersion()); + $this->assertEquals('error', $postImportResult->getPostImportStatus()); + $this->assertEquals('Test Error', $postImportResult->getPostImportMessage()); + } + + /** + * Verify Logging on Successful Datastore Drop. + * + * @covers ::postImportProcessItem + */ + public function testLoggingOnSuccessfulDatastoreDrop() { + $resource = new DataResource('test.csv', 'text/csv'); + $resource_processor = (new Chain($this)) + ->add(ResourceProcessorInterface::class, 'process') + ->getMock(); + + $container_chain = $this->getContainerChain() + ->add(ResourceProcessorCollector::class, 'getResourceProcessors', [$resource_processor]) + ->add(ResourceMapper::class, 'get', $resource); + \Drupal::setContainer($container_chain->getMock()); + + $dictionaryEnforcer = PostImportResourceProcessor::create( + $container_chain->getMock(), [], '', ['cron' => ['lease_time' => 10800]] + ); + $postImportResult = $dictionaryEnforcer->postImportProcessItem($resource); + + $this->assertEquals($resource->getIdentifier(), $postImportResult->getResourceIdentifier()); + $this->assertEquals($resource->getVersion(), $postImportResult->getResourceVersion()); + } + + /** + * Verify No Datastore Drop When drop_config is Disabled. + * + * @covers ::postImportProcessItem + */ + public function testNoDatastoreDropWhenDropConfigIsDisabled() { + $resource = new DataResource('test.csv', 'text/csv'); + $resource_processor = (new Chain($this)) + ->add(ResourceProcessorInterface::class, 'process') + ->getMock(); + + $datastoreService = $this->createMock(DatastoreService::class); + $datastoreService->expects($this->never()) + ->method('drop'); + + $container_chain = $this->getContainerChain() + ->add(ResourceProcessorCollector::class, 'getResourceProcessors', [$resource_processor]) + ->add(ResourceMapper::class, 'get', $resource); + \Drupal::setContainer($container_chain->getMock()); + + $dictionaryEnforcer = PostImportResourceProcessor::create( + $container_chain->getMock(), [], '', ['cron' => ['lease_time' => 10800]] + ); + + $postImportResult = $dictionaryEnforcer->postImportProcessItem($resource); + + $this->assertEquals('done', $postImportResult->getPostImportStatus()); + $this->assertEquals(NULL, $postImportResult->getPostImportMessage()); + } + /** * Get container chain. */ protected function getContainerChain() { $options = (new Options()) + ->add('config.factory', ConfigFactoryInterface::class) ->add('dkan.datastore.data_dictionary.alter_table_query_builder.mysql', AlterTableQueryBuilderInterface::class) ->add('dkan.metastore.data_dictionary_discovery', DataDictionaryDiscovery::class) ->add('dkan.datastore.logger_channel', LoggerInterface::class) @@ -239,6 +332,7 @@ protected function getContainerChain() { ->add('dkan.metastore.data_dictionary_discovery', DataDictionaryDiscoveryInterface::class) ->add('stream_wrapper_manager', StreamWrapperManager::class) ->add('dkan.metastore.resource_mapper', ResourceMapper::class) + ->add('dkan.datastore.service', DatastoreService::class) ->add('dkan.datastore.service.resource_processor_collector', ResourceProcessorCollector::class) ->add('dkan.datastore.service.post_import', PostImport::class) ->add('dkan.metastore.reference_lookup', ReferenceLookup::class) @@ -256,7 +350,9 @@ protected function getContainerChain() { ->add(DataDictionaryDiscoveryInterface::class, 'dictionaryIdFromResource', 'resource_id') ->add(PublicStream::class, 'getExternalUrl', self::HOST) ->add(StreamWrapperManager::class, 'getViaUri', PublicStream::class) - ->add(ResourceMapper::class, 'get', DataResource::class); + ->add(ResourceMapper::class, 'get', DataResource::class) + ->add(ConfigFactoryInterface::class, 'get', FALSE) + ->add(DatastoreService::class, 'drop'); } } diff --git a/modules/datastore/tests/src/Unit/Service/ResourceProcessor/DictionaryEnforcerTest.php b/modules/datastore/tests/src/Unit/Service/ResourceProcessor/DictionaryEnforcerTest.php index 20f42d45e7..84c9d2d418 100644 --- a/modules/datastore/tests/src/Unit/Service/ResourceProcessor/DictionaryEnforcerTest.php +++ b/modules/datastore/tests/src/Unit/Service/ResourceProcessor/DictionaryEnforcerTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\datastore\Unit\Service\ResourceProcessor; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\Container; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\StreamWrapper\StreamWrapperManager; @@ -9,6 +10,7 @@ use Drupal\datastore\DataDictionary\AlterTableQueryBuilderInterface; use Drupal\datastore\DataDictionary\AlterTableQueryInterface; use Drupal\datastore\Plugin\QueueWorker\PostImportResourceProcessor; +use Drupal\datastore\DatastoreService; use Drupal\datastore\Service\PostImport; use Drupal\datastore\Service\ResourceProcessorCollector; use Drupal\datastore\Service\ResourceProcessor\DictionaryEnforcer; @@ -150,6 +152,7 @@ public function testReturnDataDictionaryFields() { protected function getContainerChain(int $resource_version) { $options = (new Options()) + ->add('config.factory', ConfigFactoryInterface::class) ->add('dkan.datastore.data_dictionary.alter_table_query_builder.mysql', AlterTableQueryBuilderInterface::class) ->add('dkan.metastore.data_dictionary_discovery', DataDictionaryDiscovery::class) ->add('dkan.datastore.logger_channel', LoggerInterface::class) @@ -157,6 +160,7 @@ protected function getContainerChain(int $resource_version) { ->add('dkan.metastore.data_dictionary_discovery', DataDictionaryDiscoveryInterface::class) ->add('stream_wrapper_manager', StreamWrapperManager::class) ->add('dkan.metastore.resource_mapper', ResourceMapper::class) + ->add('dkan.datastore.service', DatastoreService::class) ->add('dkan.datastore.service.resource_processor_collector', ResourceProcessorCollector::class) ->add('dkan.datastore.service.resource_processor.dictionary_enforcer', DictionaryEnforcer::class) ->add('dkan.datastore.service.post_import', PostImport::class) @@ -177,7 +181,9 @@ protected function getContainerChain(int $resource_version) { ->add(PublicStream::class, 'getExternalUrl', self::HOST) ->add(StreamWrapperManager::class, 'getViaUri', PublicStream::class) ->add(ResourceMapper::class, 'get', DataResource::class) - ->add(DataResource::class, 'getVersion', $resource_version); + ->add(DataResource::class, 'getVersion', $resource_version) + ->add(ConfigFactoryInterface::class, 'get', FALSE) + ->add(DatastoreService::class, 'drop'); } } diff --git a/modules/metastore/metastore.links.menu.yml b/modules/metastore/metastore.links.menu.yml index 6d536e881e..b7186b6ffb 100644 --- a/modules/metastore/metastore.links.menu.yml +++ b/modules/metastore/metastore.links.menu.yml @@ -1,5 +1,5 @@ dkan.metastore.config_properties: - title: Metastore settings + title: Metastore Settings route_name: dkan.metastore.config_properties description: Configure dataset properties for referencing sub-schemas and for HTML sanitization. parent: system.admin_dkan diff --git a/modules/metastore/metastore.routing.yml b/modules/metastore/metastore.routing.yml index 4ab7fd01f8..81098390dc 100644 --- a/modules/metastore/metastore.routing.yml +++ b/modules/metastore/metastore.routing.yml @@ -153,7 +153,7 @@ dkan.metastore.config_properties: path: '/admin/dkan/properties' defaults: _form: '\Drupal\metastore\Form\DkanDataSettingsForm' - _title: 'Metastore settings' + _title: 'Metastore Settings' requirements: _permission: 'administer metastore settings' options: