Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dictionary discovery service #3765

Merged
merged 1 commit into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions modules/metastore/metastore.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,8 @@ services:
- '@dkan.metastore.metastore_item_factory'
- '@cache_tags.invalidator'
- '@module_handler'

dkan.metastore.data_dictionary_discovery:
class: \Drupal\metastore\DataDictionary\DataDictionaryDiscovery
arguments:
- '@config.factory'
56 changes: 56 additions & 0 deletions modules/metastore/src/DataDictionary/DataDictionaryDiscovery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Drupal\metastore\DataDictionary;

use Drupal\Core\Config\ConfigFactoryInterface;

/**
* Data dictionary service.
*
* Find the correct data dictionary for a dataset or distribution.
*/
class DataDictionaryDiscovery implements DataDictionaryDiscoveryInterface {

/**
* Constructor.
*/
public function __construct(ConfigFactoryInterface $configFactory) {
$this->config = $configFactory->get('metastore.settings');
}

/**
* {@inheritdoc}
*/
public function dictionaryIdFromResource(string $resourceId, ?int $resourceIdVersion = NULL): ?string {
$mode = $this->getDataDictionaryMode();
// For now, we only support sitewide!
switch ($mode) {
case self::MODE_NONE:
return NULL;

case self::MODE_SITEWIDE:
return $this->getSitewideDictionaryId();

default:
throw new \OutOfRangeException("Unsupported data dictionary mode " . (string) $mode);
}
}

/**
* {@inheritdoc}
*/
public function getSitewideDictionaryId(): string {
if ($identifier = $this->config->get('data_dictionary_sitewide')) {
return $identifier;
}
throw new \OutOfBoundsException("Attempted to retrieve a sitewide data dictionary, but none was set.");
}

/**
* {@inheritdoc}
*/
public function getDataDictionaryMode(): int {
return $this->config->get('data_dictionary_mode') ?? self::MODE_NONE;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Drupal\metastore\DataDictionary;

/**
* Provides interface for data dictionary discovery service.
*/
interface DataDictionaryDiscoveryInterface {

const MODE_NONE = 0;
const MODE_SITEWIDE = 1;
const MODE_COLLECTION = 2;
const MODE_GENERATE = 3;

/**
* Return the item ID for the appropriate data dictionary for a resource.
*
* @param string $resourceId
* DKAN datastore resource identifier.
* @param int|null $resourceIdVersion
* DKAN datastore resource version ID.
*
* @return string|null
* The data dictionary identifier or NULL if none exists.
*/
public function dictionaryIdFromResource(string $resourceId, ?int $resourceIdVersion = NULL): ?string;

/**
* Get the current data dictionary "mode" from DKAN config.
*
* Return values should represent modes like "single sitewide dictionary" or
* "distribution-specific dictionaries." The setting will have implications
* for behaviors in both the metastore and datastore sites of data dictionary
* functionality.
*
* @return int
* Data dictionary mode. Returns one of the MODE_* constants.
*/
public function getDataDictionaryMode(): int;

/**
* If a single sitewide data dictionary has been defined, return its ID.
*
* @return string
* Data dictionary identifier.
*/
public function getSitewideDictionaryId(): string;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Drupal\Tests\metastore\Unit\DataDictionary;

use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\metastore\DataDictionary\DataDictionaryDiscovery as Discovery;
use MockChain\Chain;
use MockChain\Options;
use PHPUnit\Framework\TestCase;

class DataDictionaryDiscoveryTest extends TestCase {


// If mode is set to "none", we should get NULL no matter what.
public function testModeNone() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_NONE, 'abc-123');
$discovery = new Discovery($configFactoryMock);
$id = $discovery->dictionaryIdFromResource('resource1');
$this->assertNull($id);
}

// If mode is sitewide, and we have a sitewide dictionary ID set, it should be
// returned, no matter what resource we pass to the method.
public function testSitewideId() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_SITEWIDE, 'abc-123');
$discovery = new Discovery($configFactoryMock);
$id = $discovery->dictionaryIdFromResource('resource1');
$this->assertEquals('abc-123', $id);
$idVersion = $discovery->dictionaryIdFromResource('resource1', '2352643');
$this->assertEquals('abc-123', $idVersion);
}

// If mode is sitewide but sitewide ID unset, we should get an exception.
public function testSitewideIdUnset() {
// Need to use 0 because MockChain\Options doesn't support NULL returns.
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_SITEWIDE, 0);
$discovery = new Discovery($configFactoryMock);

$this->expectException(\OutOfBoundsException::class);
$discovery->dictionaryIdFromResource('resource1');
}

// If mode is set to "collection", at the moment we should throw an exception
// because this is not yet supported.
public function testModeCollection() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_COLLECTION, 'abc-123');
$discovery = new Discovery($configFactoryMock);

$this->expectException(\OutOfRangeException::class);
$discovery->dictionaryIdFromResource('resource1');
}

// If mode is set to "generate", at the moment we should throw an exception
// because this is not yet supported.
public function testModeGenerate() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_GENERATE, 'abc-123');
$discovery = new Discovery($configFactoryMock);

$this->expectException(\OutOfRangeException::class);
$discovery->dictionaryIdFromResource('resource1');
}

// Build mock config service, based on arguments for mode and sitewide ID.
private function getConfigFactoryMock($mode, $sitewideId) {
$options = (new Options())
->add('data_dictionary_mode', $mode)
->add('data_dictionary_sitewide', $sitewideId)
->index(0);

return (new Chain($this))
->add(ConfigFactory::class, 'get', ImmutableConfig::class)
->add(ImmutableConfig::class, 'get', $options)
->getMock();
}
}